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