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