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