blob: 50fa094ec036058b9a766e4d4c95ee33cf8b469a [file]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room.compiler.processing
import androidx.room.compiler.codegen.JArrayTypeName
import androidx.room.compiler.codegen.XTypeName
import androidx.room.compiler.processing.javac.JavacProcessingEnv
import androidx.room.compiler.processing.ksp.KspProcessingEnv
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.squareup.kotlinpoet.javapoet.JClassName
import com.squareup.kotlinpoet.javapoet.JTypeName
import com.squareup.kotlinpoet.javapoet.KClassName
import javax.annotation.processing.ProcessingEnvironment
import kotlin.reflect.KClass
/** API for a Processor that is either backed by Java's Annotation Processing API or KSP. */
@ExperimentalProcessingApi
interface XProcessingEnv {
val backend: Backend
/** The logger interface to log messages */
val messager: XMessager
/** List of options passed into the annotation processor */
val options: Map<String, String>
/** The API to generate files */
val filer: XFiler
/** Configuration to control certain behaviors of XProcessingEnv. */
val config: XProcessingEnvConfig
/**
* Java language version of the processing environment.
*
* Value is the common JDK version representation even for the older JVM Specs named using the
* 1.x notation. i.e. for '1.8' this return 8, for '11' this returns 11, etc.
*/
val jvmVersion: Int
/**
* Information of target platforms of the processing environment.
*
* There can be multiple platforms in a metadata compilation. This is due to the fact that when
* processing `common` source sets (which will be used to compile to multiple platforms), the
* `targetPlatforms` set will contain an entry for each of the platforms the `common` code will
* be used for.
*
* If a non-common source set (e.g. linuxX64) is being processed, then `targetPlatforms` will
* contain only one entry that corresponds to the platform.
*
* For details, see the official Kotlin documentation at
* https://kotlinlang.org/docs/ksp-multiplatform.html#compilation-and-processing.
*/
val targetPlatforms: Set<Platform>
/**
* Looks for the [XTypeElement] with the given qualified name and returns `null` if it does not
* exist.
*/
fun findTypeElement(qName: String): XTypeElement?
/**
* Looks for the [XType] with the given qualified name and returns `null` if it does not exist.
*/
fun findType(qName: String): XType?
/** Returns the [XTypeElement] for the annotation that should be added to the generated code. */
fun findGeneratedAnnotation(): XTypeElement?
/**
* Returns an [XType] for the given [type] element with the type arguments specified as in
* [types].
*/
fun getDeclaredType(type: XTypeElement, vararg types: XType): XType
/**
* Returns an [XType] representing a wildcard type.
*
* In Java source, this represents types like `?`, `? extends T`, and `? super T`.
*
* In Kotlin source, this represents types like `*`, `out T`, and `in T`.
*/
fun getWildcardType(consumerSuper: XType? = null, producerExtends: XType? = null): XType
/** Return an [XArrayType] that has [type] as the [XArrayType.componentType]. */
fun getArrayType(type: XType): XArrayType
/**
* Returns the [XTypeElement] with the given qualified name or throws an exception if it does
* not exist.
*/
fun requireTypeElement(qName: String): XTypeElement {
return checkNotNull(findTypeElement(qName)) { "Cannot find required type element $qName" }
}
fun requireTypeElement(typeName: XTypeName): XTypeElement {
return checkNotNull(findTypeElement(typeName)) {
"Cannot find required type element $typeName"
}
}
fun requireTypeElement(klass: KClass<*>) = requireTypeElement(klass.java.canonicalName!!)
@Deprecated(
message = "Prefer using XTypeName or String overload instead of JavaPoet.",
replaceWith = ReplaceWith(expression = "requireTypeElement(typeName.toString())"),
)
fun requireTypeElement(typeName: JTypeName) = requireTypeElement(typeName.toString())
fun findTypeElement(typeName: XTypeName): XTypeElement? {
if (typeName.isPrimitive) {
return findTypeElement(typeName.java.toString())
}
return when (backend) {
Backend.JAVAC -> {
val jClassName =
typeName.java as? JClassName
?: error("Cannot find required type element ${typeName.java}")
findTypeElement(jClassName.canonicalName())
}
Backend.KSP -> {
val kClassName =
typeName.kotlin as? KClassName
?: error("Cannot find required type element ${typeName.kotlin}")
findTypeElement(kClassName.canonicalName)
}
}
}
fun findTypeElement(klass: KClass<*>) = findTypeElement(klass.java.canonicalName!!)
@Deprecated(
message = "Prefer using XTypeName or String overload instead of JavaPoet.",
replaceWith = ReplaceWith(expression = "findTypeElement(typeName.toString())"),
)
fun findTypeElement(typeName: JTypeName) = findTypeElement(typeName.toString())
/**
* Returns the [XType] with the given qualified name or throws an exception if it does not
* exist.
*/
fun requireType(qName: String): XType =
checkNotNull(findType(qName)) { "cannot find required type $qName" }
fun requireType(typeName: XTypeName): XType =
checkNotNull(findType(typeName)) { "cannot find required type $typeName" }
fun requireType(klass: KClass<*>) = requireType(klass.java.canonicalName!!)
@Deprecated(
message = "Prefer using XTypeName or String overload instead of JavaPoet.",
replaceWith = ReplaceWith(expression = "requireType(typeName.toString())"),
)
fun requireType(typeName: JTypeName) =
checkNotNull(findType(typeName.toString())) { "cannot find required type $typeName" }
fun findType(typeName: XTypeName): XType? {
if (typeName.isPrimitive) {
return findType(typeName.java.toString())
}
val jTypeName = typeName.java
if (jTypeName is JArrayTypeName) {
return findType(jTypeName.componentType.toString())?.let { getArrayType(it) }
}
return when (backend) {
Backend.JAVAC -> {
val jClassName =
typeName.java as? JClassName
?: error("Cannot find required type element ${typeName.java}")
findType(jClassName.canonicalName())
}
Backend.KSP -> {
val kClassName =
typeName.kotlin as? KClassName
?: error("Cannot find required type ${typeName.kotlin}")
findType(kClassName.canonicalName)
}
}?.let {
when (typeName.nullability) {
XNullability.NULLABLE -> it.makeNullable()
XNullability.NONNULL -> it.makeNonNullable()
XNullability.UNKNOWN -> it
}
}
}
fun findType(klass: KClass<*>) = findType(klass.java.canonicalName!!)
@Deprecated(
message = "Prefer using XTypeName or String overload instead of JavaPoet.",
replaceWith = ReplaceWith(expression = "findType(typeName.toString())"),
)
fun findType(typeName: JTypeName): XType? {
if (typeName is JArrayTypeName) {
return findType(typeName.componentType.toString())?.let { getArrayType(it) }
}
return findType(typeName.toString())
}
fun getArrayType(typeName: XTypeName) = getArrayType(requireType(typeName))
@Deprecated("Prefer using XTypeName or String overload instead of JavaPoet.")
fun getArrayType(typeName: JTypeName) = getArrayType(requireType(typeName.toString()))
enum class Backend {
JAVAC,
KSP,
}
enum class Platform {
JVM,
NATIVE,
JS,
UNKNOWN,
}
companion object {
/** Creates a new [XProcessingEnv] implementation derived from the given Java [env]. */
@JvmStatic
@JvmOverloads
fun create(
env: ProcessingEnvironment,
config: XProcessingEnvConfig = XProcessingEnvConfig.DEFAULT,
): XProcessingEnv = JavacProcessingEnv(env, config)
/** Creates a new [XProcessingEnv] implementation derived from the given KSP environment. */
@JvmStatic
@JvmOverloads
fun create(
symbolProcessorEnvironment: SymbolProcessorEnvironment,
resolver: Resolver,
config: XProcessingEnvConfig = XProcessingEnvConfig.DEFAULT,
): XProcessingEnv =
KspProcessingEnv(delegate = symbolProcessorEnvironment, config = config).also {
it.resolver = resolver
}
}
/**
* Returns [XTypeElement]s with the given package name. Note that this call can be expensive.
*
* @param packageName the package name to look up.
* @return A list of [XTypeElement] with matching package name. This will return declarations
* from both dependencies and source. If the package is not found an empty list will be
* returned.
*/
fun getTypeElementsFromPackage(packageName: String): List<XTypeElement>
/**
* Returns [XElement]s with the given package name. Note that this call can be expensive.
*
* @param packageName the package name to look up.
* @return A list of [XElement] with matching package name. This will return declarations from
* both dependencies and source. If the package is not found an empty list will be returned.
*/
fun getElementsFromPackage(packageName: String): List<XElement>
}