1 /*
<lambda>null2  * 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 androidx.lifecycle
18 
19 import androidx.lifecycle.model.AdapterClass
20 import androidx.lifecycle.model.EventMethod
21 import androidx.lifecycle.model.EventMethodCall
22 import androidx.lifecycle.model.InputModel
23 import androidx.lifecycle.model.LifecycleObserverInfo
24 import com.google.common.collect.HashMultimap
25 import javax.annotation.processing.ProcessingEnvironment
26 import javax.lang.model.element.TypeElement
27 import javax.tools.Diagnostic
28 
29 private fun mergeAndVerifyMethods(
30     processingEnv: ProcessingEnvironment,
31     type: TypeElement,
32     classMethods: List<EventMethod>,
33     parentMethods: List<EventMethod>
34 ): List<EventMethod> {
35     // need to update parent methods like that because:
36     // 1. visibility can be expanded
37     // 2. we want to preserve order
38     val updatedParentMethods =
39         parentMethods.map { parentMethod ->
40             val overrideMethod =
41                 classMethods.find { (method) ->
42                     processingEnv.elementUtils.overrides(method, parentMethod.method, type)
43                 }
44             if (overrideMethod != null) {
45                 if (overrideMethod.onLifecycleEvent != parentMethod.onLifecycleEvent) {
46                     processingEnv.messager.printMessage(
47                         Diagnostic.Kind.ERROR,
48                         ErrorMessages.INVALID_STATE_OVERRIDE_METHOD,
49                         overrideMethod.method
50                     )
51                 }
52                 overrideMethod
53             } else {
54                 parentMethod
55             }
56         }
57     return updatedParentMethods + classMethods.filterNot { updatedParentMethods.contains(it) }
58 }
59 
flattenObserversnull60 fun flattenObservers(
61     processingEnv: ProcessingEnvironment,
62     world: Map<TypeElement, LifecycleObserverInfo>
63 ): List<LifecycleObserverInfo> {
64     val flattened: MutableMap<LifecycleObserverInfo, LifecycleObserverInfo> = mutableMapOf()
65 
66     fun traverse(observer: LifecycleObserverInfo) {
67         if (observer in flattened) {
68             return
69         }
70         if (observer.parents.isEmpty()) {
71             flattened[observer] = observer
72             return
73         }
74         observer.parents.forEach(::traverse)
75         val methods =
76             observer.parents.map(flattened::get).fold(emptyList<EventMethod>()) {
77                 list,
78                 parentObserver ->
79                 mergeAndVerifyMethods(processingEnv, observer.type, parentObserver!!.methods, list)
80             }
81 
82         flattened[observer] =
83             LifecycleObserverInfo(
84                 observer.type,
85                 mergeAndVerifyMethods(processingEnv, observer.type, observer.methods, methods)
86             )
87     }
88 
89     world.values.forEach(::traverse)
90     return flattened.values.toList()
91 }
92 
needsSyntheticAccessnull93 private fun needsSyntheticAccess(type: TypeElement, eventMethod: EventMethod): Boolean {
94     val executable = eventMethod.method
95     return type.getPackageQName() != eventMethod.packageName() &&
96         (executable.isPackagePrivate() || executable.isProtected())
97 }
98 
validateMethodnull99 private fun validateMethod(
100     processingEnv: ProcessingEnvironment,
101     world: InputModel,
102     type: TypeElement,
103     eventMethod: EventMethod
104 ): Boolean {
105     if (!needsSyntheticAccess(type, eventMethod)) {
106         // no synthetic calls - no problems
107         return true
108     }
109 
110     if (world.isRootType(eventMethod.type)) {
111         // we will generate adapters for them, so we can generate all accessors
112         return true
113     }
114 
115     if (world.hasSyntheticAccessorFor(eventMethod)) {
116         // previously generated adapter already has synthetic
117         return true
118     }
119 
120     processingEnv.messager.printMessage(
121         Diagnostic.Kind.WARNING,
122         ErrorMessages.failedToGenerateAdapter(type, eventMethod),
123         type
124     )
125     return false
126 }
127 
transformToOutputnull128 fun transformToOutput(processingEnv: ProcessingEnvironment, world: InputModel): List<AdapterClass> {
129     val flatObservers = flattenObservers(processingEnv, world.observersInfo)
130     val syntheticMethods = HashMultimap.create<TypeElement, EventMethodCall>()
131     val adapterCalls =
132         flatObservers
133             // filter out everything that arrived from jars
134             .filter { (type) -> world.isRootType(type) }
135             // filter out if it needs SYNTHETIC access and we can't generate adapter for it
136             .filter { (type, methods) ->
137                 methods.all { eventMethod ->
138                     validateMethod(processingEnv, world, type, eventMethod)
139                 }
140             }
141             .map { (type, methods) ->
142                 val calls =
143                     methods.map { eventMethod ->
144                         if (needsSyntheticAccess(type, eventMethod)) {
145                             EventMethodCall(eventMethod, eventMethod.type)
146                         } else {
147                             EventMethodCall(eventMethod)
148                         }
149                     }
150                 calls
151                     .filter { it.syntheticAccess != null }
152                     .forEach { eventMethod ->
153                         syntheticMethods.put(eventMethod.method.type, eventMethod)
154                     }
155                 type to calls
156             }
157             .toMap()
158 
159     return adapterCalls.map { (type, calls) ->
160         val methods = syntheticMethods.get(type) ?: emptySet()
161         val synthetic = methods.map { eventMethod -> eventMethod!!.method.method }.toSet()
162         AdapterClass(type, calls, synthetic)
163     }
164 }
165