1 /*
2  * Copyright (C) 2020 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.compiler.processing
18 
19 import androidx.room.compiler.codegen.JArrayTypeName
20 import androidx.room.compiler.codegen.XTypeName
21 import androidx.room.compiler.processing.javac.JavacProcessingEnv
22 import androidx.room.compiler.processing.ksp.KspProcessingEnv
23 import com.google.devtools.ksp.processing.Resolver
24 import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
25 import com.squareup.kotlinpoet.javapoet.JClassName
26 import com.squareup.kotlinpoet.javapoet.JTypeName
27 import com.squareup.kotlinpoet.javapoet.KClassName
28 import javax.annotation.processing.ProcessingEnvironment
29 import kotlin.reflect.KClass
30 
31 /** API for a Processor that is either backed by Java's Annotation Processing API or KSP. */
32 @ExperimentalProcessingApi
33 interface XProcessingEnv {
34 
35     val backend: Backend
36 
37     /** The logger interface to log messages */
38     val messager: XMessager
39 
40     /** List of options passed into the annotation processor */
41     val options: Map<String, String>
42 
43     /** The API to generate files */
44     val filer: XFiler
45 
46     /** Configuration to control certain behaviors of XProcessingEnv. */
47     val config: XProcessingEnvConfig
48 
49     /**
50      * Java language version of the processing environment.
51      *
52      * Value is the common JDK version representation even for the older JVM Specs named using the
53      * 1.x notation. i.e. for '1.8' this return 8, for '11' this returns 11, etc.
54      */
55     val jvmVersion: Int
56 
57     /**
58      * Information of target platforms of the processing environment.
59      *
60      * There can be multiple platforms in a metadata compilation. This is due to the fact that when
61      * processing `common` source sets (which will be used to compile to multiple platforms), the
62      * `targetPlatforms` set will contain an entry for each of the platforms the `common` code will
63      * be used for.
64      *
65      * If a non-common source set (e.g. linuxX64) is being processed, then `targetPlatforms` will
66      * contain only one entry that corresponds to the platform.
67      *
68      * For details, see the official Kotlin documentation at
69      * https://kotlinlang.org/docs/ksp-multiplatform.html#compilation-and-processing.
70      */
71     val targetPlatforms: Set<Platform>
72 
73     /**
74      * Looks for the [XTypeElement] with the given qualified name and returns `null` if it does not
75      * exist.
76      */
findTypeElementnull77     fun findTypeElement(qName: String): XTypeElement?
78 
79     /**
80      * Looks for the [XType] with the given qualified name and returns `null` if it does not exist.
81      */
82     fun findType(qName: String): XType?
83 
84     /** Returns the [XTypeElement] for the annotation that should be added to the generated code. */
85     fun findGeneratedAnnotation(): XTypeElement?
86 
87     /**
88      * Returns an [XType] for the given [type] element with the type arguments specified as in
89      * [types].
90      */
91     fun getDeclaredType(type: XTypeElement, vararg types: XType): XType
92 
93     /**
94      * Returns an [XType] representing a wildcard type.
95      *
96      * In Java source, this represents types like `?`, `? extends T`, and `? super T`.
97      *
98      * In Kotlin source, this represents types like `*`, `out T`, and `in T`.
99      */
100     fun getWildcardType(consumerSuper: XType? = null, producerExtends: XType? = null): XType
101 
102     /** Return an [XArrayType] that has [type] as the [XArrayType.componentType]. */
103     fun getArrayType(type: XType): XArrayType
104 
105     /**
106      * Returns the [XTypeElement] with the given qualified name or throws an exception if it does
107      * not exist.
108      */
109     fun requireTypeElement(qName: String): XTypeElement {
110         return checkNotNull(findTypeElement(qName)) { "Cannot find required type element $qName" }
111     }
112 
requireTypeElementnull113     fun requireTypeElement(typeName: XTypeName): XTypeElement {
114         return checkNotNull(findTypeElement(typeName)) {
115             "Cannot find required type element $typeName"
116         }
117     }
118 
requireTypeElementnull119     fun requireTypeElement(klass: KClass<*>) = requireTypeElement(klass.java.canonicalName!!)
120 
121     @Deprecated(
122         message = "Prefer using XTypeName or String overload instead of JavaPoet.",
123         replaceWith = ReplaceWith(expression = "requireTypeElement(typeName.toString())")
124     )
125     fun requireTypeElement(typeName: JTypeName) = requireTypeElement(typeName.toString())
126 
127     fun findTypeElement(typeName: XTypeName): XTypeElement? {
128         if (typeName.isPrimitive) {
129             return findTypeElement(typeName.java.toString())
130         }
131         return when (backend) {
132             Backend.JAVAC -> {
133                 val jClassName =
134                     typeName.java as? JClassName
135                         ?: error("Cannot find required type element ${typeName.java}")
136                 findTypeElement(jClassName.canonicalName())
137             }
138             Backend.KSP -> {
139                 val kClassName =
140                     typeName.kotlin as? KClassName
141                         ?: error("Cannot find required type element ${typeName.kotlin}")
142                 findTypeElement(kClassName.canonicalName)
143             }
144         }
145     }
146 
findTypeElementnull147     fun findTypeElement(klass: KClass<*>) = findTypeElement(klass.java.canonicalName!!)
148 
149     @Deprecated(
150         message = "Prefer using XTypeName or String overload instead of JavaPoet.",
151         replaceWith = ReplaceWith(expression = "findTypeElement(typeName.toString())")
152     )
153     fun findTypeElement(typeName: JTypeName) = findTypeElement(typeName.toString())
154 
155     /**
156      * Returns the [XType] with the given qualified name or throws an exception if it does not
157      * exist.
158      */
159     fun requireType(qName: String): XType =
160         checkNotNull(findType(qName)) { "cannot find required type $qName" }
161 
requireTypenull162     fun requireType(typeName: XTypeName): XType =
163         checkNotNull(findType(typeName)) { "cannot find required type $typeName" }
164 
requireTypenull165     fun requireType(klass: KClass<*>) = requireType(klass.java.canonicalName!!)
166 
167     @Deprecated(
168         message = "Prefer using XTypeName or String overload instead of JavaPoet.",
169         replaceWith = ReplaceWith(expression = "requireType(typeName.toString())")
170     )
171     fun requireType(typeName: JTypeName) =
172         checkNotNull(findType(typeName.toString())) { "cannot find required type $typeName" }
173 
findTypenull174     fun findType(typeName: XTypeName): XType? {
175         if (typeName.isPrimitive) {
176             return findType(typeName.java.toString())
177         }
178         val jTypeName = typeName.java
179         if (jTypeName is JArrayTypeName) {
180             return findType(jTypeName.componentType.toString())?.let { getArrayType(it) }
181         }
182         return when (backend) {
183             Backend.JAVAC -> {
184                 val jClassName =
185                     typeName.java as? JClassName
186                         ?: error("Cannot find required type element ${typeName.java}")
187                 findType(jClassName.canonicalName())
188             }
189             Backend.KSP -> {
190                 val kClassName =
191                     typeName.kotlin as? KClassName
192                         ?: error("Cannot find required type ${typeName.kotlin}")
193                 findType(kClassName.canonicalName)
194             }
195         }?.let {
196             when (typeName.nullability) {
197                 XNullability.NULLABLE -> it.makeNullable()
198                 XNullability.NONNULL -> it.makeNonNullable()
199                 XNullability.UNKNOWN -> it
200             }
201         }
202     }
203 
findTypenull204     fun findType(klass: KClass<*>) = findType(klass.java.canonicalName!!)
205 
206     @Deprecated(
207         message = "Prefer using XTypeName or String overload instead of JavaPoet.",
208         replaceWith = ReplaceWith(expression = "findType(typeName.toString())")
209     )
210     fun findType(typeName: JTypeName): XType? {
211         if (typeName is JArrayTypeName) {
212             return findType(typeName.componentType.toString())?.let { getArrayType(it) }
213         }
214         return findType(typeName.toString())
215     }
216 
getArrayTypenull217     fun getArrayType(typeName: XTypeName) = getArrayType(requireType(typeName))
218 
219     @Deprecated("Prefer using XTypeName or String overload instead of JavaPoet.")
220     fun getArrayType(typeName: JTypeName) = getArrayType(requireType(typeName.toString()))
221 
222     enum class Backend {
223         JAVAC,
224         KSP
225     }
226 
227     enum class Platform {
228         JVM,
229         NATIVE,
230         JS,
231         UNKNOWN
232     }
233 
234     companion object {
235         /** Creates a new [XProcessingEnv] implementation derived from the given Java [env]. */
236         @JvmStatic
237         @JvmOverloads
createnull238         fun create(
239             env: ProcessingEnvironment,
240             config: XProcessingEnvConfig = XProcessingEnvConfig.DEFAULT
241         ): XProcessingEnv = JavacProcessingEnv(env, config)
242 
243         /** Creates a new [XProcessingEnv] implementation derived from the given KSP environment. */
244         @JvmStatic
245         @JvmOverloads
246         fun create(
247             symbolProcessorEnvironment: SymbolProcessorEnvironment,
248             resolver: Resolver,
249             config: XProcessingEnvConfig = XProcessingEnvConfig.DEFAULT
250         ): XProcessingEnv =
251             KspProcessingEnv(delegate = symbolProcessorEnvironment, config = config).also {
252                 it.resolver = resolver
253             }
254     }
255 
256     /**
257      * Returns [XTypeElement]s with the given package name. Note that this call can be expensive.
258      *
259      * @param packageName the package name to look up.
260      * @return A list of [XTypeElement] with matching package name. This will return declarations
261      *   from both dependencies and source. If the package is not found an empty list will be
262      *   returned.
263      */
getTypeElementsFromPackagenull264     fun getTypeElementsFromPackage(packageName: String): List<XTypeElement>
265 
266     /**
267      * Returns [XElement]s with the given package name. Note that this call can be expensive.
268      *
269      * @param packageName the package name to look up.
270      * @return A list of [XElement] with matching package name. This will return declarations from
271      *   both dependencies and source. If the package is not found an empty list will be returned.
272      */
273     fun getElementsFromPackage(packageName: String): List<XElement>
274 }
275