1 /* <lambda>null2 * Copyright (C) 2016 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.processor 18 19 import androidx.room.Delete 20 import androidx.room.Insert 21 import androidx.room.Query 22 import androidx.room.RawQuery 23 import androidx.room.SkipQueryVerification 24 import androidx.room.Transaction 25 import androidx.room.Update 26 import androidx.room.ext.findKotlinDefaultImpl 27 import androidx.room.ext.hasAnnotation 28 import androidx.room.ext.hasAnyOf 29 import androidx.room.ext.typeName 30 import androidx.room.verifier.DatabaseVerifier 31 import androidx.room.vo.Dao 32 import com.google.auto.common.MoreElements 33 import com.google.auto.common.MoreTypes 34 import com.squareup.javapoet.TypeName 35 import javax.lang.model.element.ElementKind 36 import javax.lang.model.element.ExecutableElement 37 import javax.lang.model.element.Modifier.ABSTRACT 38 import javax.lang.model.element.TypeElement 39 import javax.lang.model.type.DeclaredType 40 41 class DaoProcessor(baseContext: Context, val element: TypeElement, val dbType: DeclaredType, 42 val dbVerifier: DatabaseVerifier?) { 43 val context = baseContext.fork(element) 44 45 companion object { 46 val PROCESSED_ANNOTATIONS = listOf(Insert::class, Delete::class, Query::class, 47 Update::class, RawQuery::class) 48 } 49 50 fun process(): Dao { 51 context.checker.hasAnnotation(element, androidx.room.Dao::class, 52 ProcessorErrors.DAO_MUST_BE_ANNOTATED_WITH_DAO) 53 context.checker.check(element.hasAnyOf(ABSTRACT) || element.kind == ElementKind.INTERFACE, 54 element, ProcessorErrors.DAO_MUST_BE_AN_ABSTRACT_CLASS_OR_AN_INTERFACE) 55 56 val declaredType = MoreTypes.asDeclared(element.asType()) 57 val allMembers = context.processingEnv.elementUtils.getAllMembers(element) 58 val methods = allMembers 59 .filter { 60 it.hasAnyOf(ABSTRACT) && it.kind == ElementKind.METHOD 61 && it.findKotlinDefaultImpl(context.processingEnv.typeUtils) == null 62 }.map { 63 MoreElements.asExecutable(it) 64 }.groupBy { method -> 65 context.checker.check( 66 PROCESSED_ANNOTATIONS.count { method.hasAnnotation(it) } == 1, method, 67 ProcessorErrors.INVALID_ANNOTATION_COUNT_IN_DAO_METHOD 68 ) 69 if (method.hasAnnotation(Query::class)) { 70 Query::class 71 } else if (method.hasAnnotation(Insert::class)) { 72 Insert::class 73 } else if (method.hasAnnotation(Delete::class)) { 74 Delete::class 75 } else if (method.hasAnnotation(Update::class)) { 76 Update::class 77 } else if (method.hasAnnotation(RawQuery::class)) { 78 RawQuery::class 79 } else { 80 Any::class 81 } 82 } 83 val processorVerifier = if (element.hasAnnotation(SkipQueryVerification::class) || 84 element.hasAnnotation(RawQuery::class)) { 85 null 86 } else { 87 dbVerifier 88 } 89 90 val queryMethods = methods[Query::class]?.map { 91 QueryMethodProcessor( 92 baseContext = context, 93 containing = declaredType, 94 executableElement = it, 95 dbVerifier = processorVerifier).process() 96 } ?: emptyList() 97 98 val rawQueryMethods = methods[RawQuery::class]?.map { 99 RawQueryMethodProcessor( 100 baseContext = context, 101 containing = declaredType, 102 executableElement = it 103 ).process() 104 } ?: emptyList() 105 106 val insertionMethods = methods[Insert::class]?.map { 107 InsertionMethodProcessor( 108 baseContext = context, 109 containing = declaredType, 110 executableElement = it).process() 111 } ?: emptyList() 112 113 val deletionMethods = methods[Delete::class]?.map { 114 DeletionMethodProcessor( 115 baseContext = context, 116 containing = declaredType, 117 executableElement = it).process() 118 } ?: emptyList() 119 120 val updateMethods = methods[Update::class]?.map { 121 UpdateMethodProcessor( 122 baseContext = context, 123 containing = declaredType, 124 executableElement = it).process() 125 } ?: emptyList() 126 127 val transactionMethods = allMembers.filter { member -> 128 member.hasAnnotation(Transaction::class) 129 && member.kind == ElementKind.METHOD 130 && PROCESSED_ANNOTATIONS.none { member.hasAnnotation(it) } 131 }.map { 132 TransactionMethodProcessor( 133 baseContext = context, 134 containing = declaredType, 135 executableElement = MoreElements.asExecutable(it)).process() 136 } 137 138 val constructors = allMembers 139 .filter { it.kind == ElementKind.CONSTRUCTOR } 140 .map { MoreElements.asExecutable(it) } 141 val typeUtils = context.processingEnv.typeUtils 142 val goodConstructor = constructors.firstOrNull { 143 it.parameters.size == 1 144 && typeUtils.isAssignable(dbType, it.parameters[0].asType()) 145 } 146 val constructorParamType = if (goodConstructor != null) { 147 goodConstructor.parameters[0].asType().typeName() 148 } else { 149 validateEmptyConstructor(constructors) 150 null 151 } 152 153 context.checker.check(methods[Any::class] == null, element, 154 ProcessorErrors.ABSTRACT_METHOD_IN_DAO_MISSING_ANY_ANNOTATION) 155 156 val type = TypeName.get(declaredType) 157 context.checker.notUnbound(type, element, 158 ProcessorErrors.CANNOT_USE_UNBOUND_GENERICS_IN_DAO_CLASSES) 159 160 return Dao(element = element, 161 type = declaredType, 162 queryMethods = queryMethods, 163 rawQueryMethods = rawQueryMethods, 164 insertionMethods = insertionMethods, 165 deletionMethods = deletionMethods, 166 updateMethods = updateMethods, 167 transactionMethods = transactionMethods, 168 constructorParamType = constructorParamType) 169 } 170 171 private fun validateEmptyConstructor(constructors: List<ExecutableElement>) { 172 if (constructors.isNotEmpty() && constructors.all { it.parameters.isNotEmpty() }) { 173 context.logger.e(element, ProcessorErrors.daoMustHaveMatchingConstructor( 174 element.toString(), dbType.toString())) 175 } 176 } 177 } 178