aboutsummaryrefslogtreecommitdiff
path: root/kotlinpoet
diff options
context:
space:
mode:
authorZac Sweers <zac.sweers@gmail.com>2021-05-06 11:04:22 -0400
committerGitHub <noreply@github.com>2021-05-06 11:04:22 -0400
commit2c072cfb2d06894a75ef5191315c3c19b561c011 (patch)
tree58e4dbd9c3bbdd962ba9108b209ee030c8620dcf /kotlinpoet
parented7a2cb3c8d8f7aa440457be4e469dab0dfb45df (diff)
downloadkotlinpoet-2c072cfb2d06894a75ef5191315c3c19b561c011.tar.gz
Update Kotlin + kotlinx-metadata to 1.5 and 0.3.0 (#1079)
Diffstat (limited to 'kotlinpoet')
-rw-r--r--kotlinpoet/src/main/java/com/squareup/kotlinpoet/ClassName.kt183
-rw-r--r--kotlinpoet/src/main/java/com/squareup/kotlinpoet/Dynamic.kt16
-rw-r--r--kotlinpoet/src/main/java/com/squareup/kotlinpoet/LambdaTypeName.kt95
-rw-r--r--kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterizedTypeName.kt171
-rw-r--r--kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeName.kt740
-rw-r--r--kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt197
-rw-r--r--kotlinpoet/src/main/java/com/squareup/kotlinpoet/WildcardTypeName.kt90
-rw-r--r--kotlinpoet/src/main/java/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt8
-rw-r--r--kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotationSpecTest.kt16
-rw-r--r--kotlinpoet/src/test/java/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt69
10 files changed, 837 insertions, 748 deletions
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ClassName.kt b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ClassName.kt
index ae64bbbc..8a5ce510 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ClassName.kt
+++ b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ClassName.kt
@@ -26,6 +26,189 @@ import javax.lang.model.element.TypeElement
import kotlin.DeprecationLevel.WARNING
import kotlin.reflect.KClass
+/** A fully-qualified class name for top-level and member classes. */
+public class ClassName internal constructor(
+ names: List<String>,
+ nullable: Boolean = false,
+ annotations: List<AnnotationSpec> = emptyList(),
+ tags: Map<KClass<*>, Any> = emptyMap()
+) : TypeName(nullable, annotations, TagMap(tags)), Comparable<ClassName> {
+ /**
+ * Returns a class name created from the given parts. For example, calling this with package name
+ * `"java.util"` and simple names `"Map"`, `"Entry"` yields `Map.Entry`.
+ */
+ @Deprecated("", level = DeprecationLevel.HIDDEN)
+ public constructor(packageName: String, simpleName: String, vararg simpleNames: String) :
+ this(listOf(packageName, simpleName, *simpleNames))
+
+ /**
+ * Returns a class name created from the given parts. For example, calling this with package name
+ * `"java.util"` and simple names `"Map"`, `"Entry"` yields `Map.Entry`.
+ */
+ public constructor(packageName: String, vararg simpleNames: String) :
+ this(listOf(packageName, *simpleNames)) {
+ require(simpleNames.isNotEmpty()) { "simpleNames must not be empty" }
+ require(simpleNames.none { it.isEmpty() }) {
+ "simpleNames must not contain empty items: ${simpleNames.contentToString()}"
+ }
+ }
+
+ /**
+ * Returns a class name created from the given parts. For example, calling this with package name
+ * `"java.util"` and simple names `"Map"`, `"Entry"` yields `Map.Entry`.
+ */
+ public constructor(packageName: String, simpleNames: List<String>) :
+ this(mutableListOf(packageName).apply { addAll(simpleNames) }) {
+ require(simpleNames.isNotEmpty()) { "simpleNames must not be empty" }
+ require(simpleNames.none { it.isEmpty() }) {
+ "simpleNames must not contain empty items: $simpleNames"
+ }
+ }
+
+ /** From top to bottom. This will be `["java.util", "Map", "Entry"]` for `Map.Entry`. */
+ private val names = names.toImmutableList()
+
+ /** Fully qualified name using `.` as a separator, like `kotlin.collections.Map.Entry`. */
+ public val canonicalName: String = if (names[0].isEmpty())
+ names.subList(1, names.size).joinToString(".") else
+ names.joinToString(".")
+
+ /** Package name, like `"kotlin.collections"` for `Map.Entry`. */
+ public val packageName: String get() = names[0]
+
+ /** Simple name of this class, like `"Entry"` for `Map.Entry`. */
+ public val simpleName: String get() = names[names.size - 1]
+
+ /**
+ * The enclosing classes, outermost first, followed by the simple name. This is `["Map", "Entry"]`
+ * for `Map.Entry`.
+ */
+ public val simpleNames: List<String> get() = names.subList(1, names.size)
+
+ override fun copy(
+ nullable: Boolean,
+ annotations: List<AnnotationSpec>,
+ tags: Map<KClass<*>, Any>
+ ): ClassName {
+ return ClassName(names, nullable, annotations, tags)
+ }
+
+ /**
+ * Returns the enclosing class, like `Map` for `Map.Entry`. Returns null if this class is not
+ * nested in another class.
+ */
+ public fun enclosingClassName(): ClassName? {
+ return if (names.size != 2)
+ ClassName(names.subList(0, names.size - 1)) else
+ null
+ }
+
+ /**
+ * Returns the top class in this nesting group. Equivalent to chained calls to
+ * [ClassName.enclosingClassName] until the result's enclosing class is null.
+ */
+ public fun topLevelClassName(): ClassName = ClassName(names.subList(0, 2))
+
+ /**
+ * Fully qualified name using `.` to separate package from the top level class name, and `$` to
+ * separate nested classes, like `kotlin.collections.Map$Entry`.
+ */
+ public fun reflectionName(): String {
+ // trivial case: no nested names
+ if (names.size == 2) {
+ return if (packageName.isEmpty())
+ names[1] else
+ packageName + "." + names[1]
+ }
+ // concat top level class name and nested names
+ return buildString {
+ append(topLevelClassName().canonicalName)
+ for (name in simpleNames.subList(1, simpleNames.size)) {
+ append('$').append(name)
+ }
+ }
+ }
+
+ /**
+ * Callable reference to the constructor of this class. Emits the enclosing class if one exists,
+ * followed by the reference operator `::`, followed by either [simpleName] or the
+ * fully-qualified name if this is a top-level class.
+ *
+ * Note: As `::$packageName.$simpleName` is not valid syntax, an aliased import may be required
+ * for a top-level class with a conflicting name.
+ */
+ public fun constructorReference(): CodeBlock {
+ val enclosing = enclosingClassName()
+ return if (enclosing != null) {
+ CodeBlock.of("%T::%N", enclosing, simpleName)
+ } else {
+ CodeBlock.of("::%T", this)
+ }
+ }
+
+ /** Returns a new [ClassName] instance for the specified `name` as nested inside this class. */
+ public fun nestedClass(name: String): ClassName = ClassName(names + name)
+
+ /**
+ * Returns a class that shares the same enclosing package or class. If this class is enclosed by
+ * another class, this is equivalent to `enclosingClassName().nestedClass(name)`. Otherwise
+ * it is equivalent to `get(packageName(), name)`.
+ */
+ public fun peerClass(name: String): ClassName {
+ val result = names.toMutableList()
+ result[result.size - 1] = name
+ return ClassName(result)
+ }
+
+ /**
+ * Orders by the fully-qualified name. Nested types are ordered immediately after their
+ * enclosing type. For example, the following types are ordered by this method:
+ *
+ * ```
+ * com.example.Robot
+ * com.example.Robot.Motor
+ * com.example.RoboticVacuum
+ * ```
+ */
+ override fun compareTo(other: ClassName): Int = canonicalName.compareTo(other.canonicalName)
+
+ override fun emit(out: CodeWriter) =
+ out.emit(out.lookupName(this).escapeSegmentsIfNecessary())
+
+ public companion object {
+ /**
+ * Returns a new [ClassName] instance for the given fully-qualified class name string. This
+ * method assumes that the input is ASCII and follows typical Java style (lowercase package
+ * names, UpperCamelCase class names) and may produce incorrect results or throw
+ * [IllegalArgumentException] otherwise. For that reason, the constructor should be preferred as
+ * it can create [ClassName] instances without such restrictions.
+ */
+ @JvmStatic public fun bestGuess(classNameString: String): ClassName {
+ val names = mutableListOf<String>()
+
+ // Add the package name, like "java.util.concurrent", or "" for no package.
+ var p = 0
+ while (p < classNameString.length && Character.isLowerCase(classNameString.codePointAt(p))) {
+ p = classNameString.indexOf('.', p) + 1
+ require(p != 0) { "couldn't make a guess for $classNameString" }
+ }
+ names += if (p != 0) classNameString.substring(0, p - 1) else ""
+
+ // Add the class names, like "Map" and "Entry".
+ for (part in classNameString.substring(p).split('.')) {
+ require(part.isNotEmpty() && Character.isUpperCase(part.codePointAt(0))) {
+ "couldn't make a guess for $classNameString"
+ }
+
+ names += part
+ }
+
+ require(names.size >= 2) { "couldn't make a guess for $classNameString" }
+ return ClassName(names)
+ }
+ }
+}
+
@JvmName("get")
public fun Class<*>.asClassName(): ClassName {
require(!isPrimitive) { "primitive types cannot be represented as a ClassName" }
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Dynamic.kt b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Dynamic.kt
new file mode 100644
index 00000000..1509e918
--- /dev/null
+++ b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Dynamic.kt
@@ -0,0 +1,16 @@
+package com.squareup.kotlinpoet
+
+import kotlin.reflect.KClass
+
+public object Dynamic : TypeName(false, emptyList(), TagMap(emptyMap())) {
+
+ override fun copy(
+ nullable: Boolean,
+ annotations: List<AnnotationSpec>,
+ tags: Map<KClass<*>, Any>
+ ): Nothing = throw UnsupportedOperationException("dynamic doesn't support copying")
+
+ override fun emit(out: CodeWriter) = out.apply {
+ emit("dynamic")
+ }
+}
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/LambdaTypeName.kt b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/LambdaTypeName.kt
new file mode 100644
index 00000000..b8c4a3e1
--- /dev/null
+++ b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/LambdaTypeName.kt
@@ -0,0 +1,95 @@
+package com.squareup.kotlinpoet
+
+import kotlin.reflect.KClass
+
+public class LambdaTypeName private constructor(
+ public val receiver: TypeName? = null,
+ parameters: List<ParameterSpec> = emptyList(),
+ public val returnType: TypeName = UNIT,
+ nullable: Boolean = false,
+ public val isSuspending: Boolean = false,
+ annotations: List<AnnotationSpec> = emptyList(),
+ tags: Map<KClass<*>, Any> = emptyMap()
+) : TypeName(nullable, annotations, TagMap(tags)) {
+ public val parameters: List<ParameterSpec> = parameters.toImmutableList()
+
+ init {
+ for (param in parameters) {
+ require(param.annotations.isEmpty()) { "Parameters with annotations are not allowed" }
+ require(param.modifiers.isEmpty()) { "Parameters with modifiers are not allowed" }
+ require(param.defaultValue == null) { "Parameters with default values are not allowed" }
+ }
+ }
+
+ override fun copy(
+ nullable: Boolean,
+ annotations: List<AnnotationSpec>,
+ tags: Map<KClass<*>, Any>
+ ): LambdaTypeName {
+ return copy(nullable, annotations, this.isSuspending, tags)
+ }
+
+ public fun copy(
+ nullable: Boolean = this.isNullable,
+ annotations: List<AnnotationSpec> = this.annotations.toList(),
+ suspending: Boolean = this.isSuspending,
+ tags: Map<KClass<*>, Any> = this.tags.toMap()
+ ): LambdaTypeName {
+ return LambdaTypeName(receiver, parameters, returnType, nullable, suspending, annotations, tags)
+ }
+
+ override fun emit(out: CodeWriter): CodeWriter {
+ if (isNullable) {
+ out.emit("(")
+ }
+
+ if (isSuspending) {
+ out.emit("suspend ")
+ }
+
+ receiver?.let {
+ if (it.isAnnotated) {
+ out.emitCode("(%T).", it)
+ } else {
+ out.emitCode("%T.", it)
+ }
+ }
+
+ parameters.emit(out)
+ out.emitCode(if (returnType is LambdaTypeName) " -> (%T)" else " -> %T", returnType)
+
+ if (isNullable) {
+ out.emit(")")
+ }
+ return out
+ }
+
+ public companion object {
+ /** Returns a lambda type with `returnType` and parameters listed in `parameters`. */
+ @JvmStatic public fun get(
+ receiver: TypeName? = null,
+ parameters: List<ParameterSpec> = emptyList(),
+ returnType: TypeName
+ ): LambdaTypeName = LambdaTypeName(receiver, parameters, returnType)
+
+ /** Returns a lambda type with `returnType` and parameters listed in `parameters`. */
+ @JvmStatic public fun get(
+ receiver: TypeName? = null,
+ vararg parameters: TypeName = emptyArray(),
+ returnType: TypeName
+ ): LambdaTypeName {
+ return LambdaTypeName(
+ receiver,
+ parameters.toList().map { ParameterSpec.unnamed(it) },
+ returnType
+ )
+ }
+
+ /** Returns a lambda type with `returnType` and parameters listed in `parameters`. */
+ @JvmStatic public fun get(
+ receiver: TypeName? = null,
+ vararg parameters: ParameterSpec = emptyArray(),
+ returnType: TypeName
+ ): LambdaTypeName = LambdaTypeName(receiver, parameters.toList(), returnType)
+ }
+}
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterizedTypeName.kt b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterizedTypeName.kt
index 1b9d69fd..757241a1 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterizedTypeName.kt
+++ b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterizedTypeName.kt
@@ -17,10 +17,181 @@
package com.squareup.kotlinpoet
+import java.lang.reflect.Modifier
import java.lang.reflect.ParameterizedType
+import java.lang.reflect.Type
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.KTypeParameter
+import kotlin.reflect.KTypeProjection
+import kotlin.reflect.KVariance
+
+public class ParameterizedTypeName internal constructor(
+ private val enclosingType: TypeName?,
+ public val rawType: ClassName,
+ typeArguments: List<TypeName>,
+ nullable: Boolean = false,
+ annotations: List<AnnotationSpec> = emptyList(),
+ tags: Map<KClass<*>, Any> = emptyMap()
+) : TypeName(nullable, annotations, TagMap(tags)) {
+ public val typeArguments: List<TypeName> = typeArguments.toImmutableList()
+
+ init {
+ require(typeArguments.isNotEmpty() || enclosingType != null) {
+ "no type arguments: $rawType"
+ }
+ }
+
+ override fun copy(
+ nullable: Boolean,
+ annotations: List<AnnotationSpec>,
+ tags: Map<KClass<*>, Any>
+ ): ParameterizedTypeName {
+ return ParameterizedTypeName(enclosingType, rawType, typeArguments, nullable, annotations, tags)
+ }
+
+ public fun plusParameter(typeArgument: TypeName): ParameterizedTypeName =
+ ParameterizedTypeName(
+ enclosingType, rawType, typeArguments + typeArgument, isNullable,
+ annotations
+ )
+
+ public fun plusParameter(typeArgument: KClass<*>): ParameterizedTypeName =
+ plusParameter(typeArgument.asClassName())
+
+ public fun plusParameter(typeArgument: Class<*>): ParameterizedTypeName =
+ plusParameter(typeArgument.asClassName())
+
+ override fun emit(out: CodeWriter): CodeWriter {
+ if (enclosingType != null) {
+ enclosingType.emitAnnotations(out)
+ enclosingType.emit(out)
+ out.emit("." + rawType.simpleName)
+ } else {
+ rawType.emitAnnotations(out)
+ rawType.emit(out)
+ }
+ if (typeArguments.isNotEmpty()) {
+ out.emit("<")
+ typeArguments.forEachIndexed { index, parameter ->
+ if (index > 0) out.emit(", ")
+ parameter.emitAnnotations(out)
+ parameter.emit(out)
+ parameter.emitNullable(out)
+ }
+ out.emit(">")
+ }
+ return out
+ }
+
+ /**
+ * Returns a new [ParameterizedTypeName] instance for the specified `name` as nested inside this
+ * class, with the specified `typeArguments`.
+ */
+ public fun nestedClass(name: String, typeArguments: List<TypeName>): ParameterizedTypeName =
+ ParameterizedTypeName(this, rawType.nestedClass(name), typeArguments)
+
+ public companion object {
+ /** Returns a parameterized type, applying `typeArguments` to `this`. */
+ @JvmStatic @JvmName("get") public fun ClassName.parameterizedBy(
+ vararg typeArguments: TypeName
+ ): ParameterizedTypeName = ParameterizedTypeName(null, this, typeArguments.toList())
+
+ /** Returns a parameterized type, applying `typeArguments` to `this`. */
+ @JvmStatic @JvmName("get") public fun KClass<*>.parameterizedBy(
+ vararg typeArguments: KClass<*>
+ ): ParameterizedTypeName =
+ ParameterizedTypeName(null, asClassName(), typeArguments.map { it.asTypeName() })
+
+ /** Returns a parameterized type, applying `typeArguments` to `this`. */
+ @JvmStatic @JvmName("get") public fun Class<*>.parameterizedBy(
+ vararg typeArguments: Type
+ ): ParameterizedTypeName =
+ ParameterizedTypeName(null, asClassName(), typeArguments.map { it.asTypeName() })
+
+ /** Returns a parameterized type, applying `typeArguments` to `this`. */
+ @JvmStatic @JvmName("get") public fun ClassName.parameterizedBy(
+ typeArguments: List<TypeName>
+ ): ParameterizedTypeName = ParameterizedTypeName(null, this, typeArguments)
+
+ /** Returns a parameterized type, applying `typeArguments` to `this`. */
+ @JvmStatic @JvmName("get") public fun KClass<*>.parameterizedBy(
+ typeArguments: Iterable<KClass<*>>
+ ): ParameterizedTypeName =
+ ParameterizedTypeName(null, asClassName(), typeArguments.map { it.asTypeName() })
+
+ /** Returns a parameterized type, applying `typeArguments` to `this`. */
+ @JvmStatic @JvmName("get") public fun Class<*>.parameterizedBy(
+ typeArguments: Iterable<Type>
+ ): ParameterizedTypeName =
+ ParameterizedTypeName(null, asClassName(), typeArguments.map { it.asTypeName() })
+
+ /** Returns a parameterized type, applying `typeArgument` to `this`. */
+ @JvmStatic @JvmName("get") public fun ClassName.plusParameter(
+ typeArgument: TypeName
+ ): ParameterizedTypeName = parameterizedBy(typeArgument)
+
+ /** Returns a parameterized type, applying `typeArgument` to `this`. */
+ @JvmStatic @JvmName("get") public fun KClass<*>.plusParameter(
+ typeArgument: KClass<*>
+ ): ParameterizedTypeName = parameterizedBy(typeArgument)
+
+ /** Returns a parameterized type, applying `typeArgument` to `this`. */
+ @JvmStatic @JvmName("get") public fun Class<*>.plusParameter(
+ typeArgument: Class<*>
+ ): ParameterizedTypeName = parameterizedBy(typeArgument)
+
+ /** Returns a parameterized type equivalent to `type`. */
+ internal fun get(
+ type: ParameterizedType,
+ map: MutableMap<Type, TypeVariableName>
+ ): ParameterizedTypeName {
+ val rawType = (type.rawType as Class<*>).asClassName()
+ val ownerType = if (type.ownerType is ParameterizedType &&
+ !Modifier.isStatic((type.rawType as Class<*>).modifiers)
+ )
+ type.ownerType as ParameterizedType else
+ null
+
+ val typeArguments = type.actualTypeArguments.map { get(it, map = map) }
+ return if (ownerType != null)
+ get(ownerType, map = map).nestedClass(rawType.simpleName, typeArguments) else
+ ParameterizedTypeName(null, rawType, typeArguments)
+ }
+
+ /** Returns a type name equivalent to type with given list of type arguments. */
+ internal fun get(
+ type: KClass<*>,
+ nullable: Boolean,
+ typeArguments: List<KTypeProjection>
+ ): TypeName {
+ if (typeArguments.isEmpty()) {
+ return type.asTypeName().run { if (nullable) copy(nullable = true) else this }
+ }
+
+ val effectiveType = if (type.java.isArray) Array<Unit>::class else type
+ val enclosingClass = type.java.enclosingClass?.kotlin
+
+ return ParameterizedTypeName(
+ enclosingClass?.let {
+ get(it, false, typeArguments.drop(effectiveType.typeParameters.size))
+ },
+ effectiveType.asTypeName(),
+ typeArguments.take(effectiveType.typeParameters.size).map { (paramVariance, paramType) ->
+ val typeName = paramType?.asTypeName() ?: return@map STAR
+ when (paramVariance) {
+ null -> STAR
+ KVariance.INVARIANT -> typeName
+ KVariance.IN -> WildcardTypeName.consumerOf(typeName)
+ KVariance.OUT -> WildcardTypeName.producerOf(typeName)
+ }
+ },
+ nullable,
+ effectiveType.annotations.map { AnnotationSpec.get(it) }
+ )
+ }
+ }
+}
/** Returns a parameterized type equivalent to `type`. */
@JvmName("get")
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeName.kt b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeName.kt
index 7bf5969d..26000030 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeName.kt
+++ b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeName.kt
@@ -19,12 +19,10 @@ package com.squareup.kotlinpoet
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import java.lang.reflect.GenericArrayType
-import java.lang.reflect.Modifier.isStatic
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.lang.reflect.TypeVariable
import java.lang.reflect.WildcardType
-import java.util.Collections
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
import javax.lang.model.element.TypeParameterElement
@@ -38,8 +36,6 @@ import javax.lang.model.type.TypeMirror
import javax.lang.model.util.SimpleTypeVisitor7
import kotlin.DeprecationLevel.WARNING
import kotlin.reflect.KClass
-import kotlin.reflect.KTypeProjection
-import kotlin.reflect.KVariance
import kotlin.reflect.typeOf
/**
@@ -298,739 +294,3 @@ public fun Type.asTypeName(): TypeName = TypeName.get(this, mutableMapOf())
@ExperimentalStdlibApi
public inline fun <reified T> typeNameOf(): TypeName = typeOf<T>().asTypeName()
-
-/** A fully-qualified class name for top-level and member classes. */
-public class ClassName internal constructor(
- names: List<String>,
- nullable: Boolean = false,
- annotations: List<AnnotationSpec> = emptyList(),
- tags: Map<KClass<*>, Any> = emptyMap()
-) : TypeName(nullable, annotations, TagMap(tags)), Comparable<ClassName> {
- /**
- * Returns a class name created from the given parts. For example, calling this with package name
- * `"java.util"` and simple names `"Map"`, `"Entry"` yields `Map.Entry`.
- */
- @Deprecated("", level = DeprecationLevel.HIDDEN)
- public constructor(packageName: String, simpleName: String, vararg simpleNames: String) :
- this(listOf(packageName, simpleName, *simpleNames))
-
- /**
- * Returns a class name created from the given parts. For example, calling this with package name
- * `"java.util"` and simple names `"Map"`, `"Entry"` yields `Map.Entry`.
- */
- public constructor(packageName: String, vararg simpleNames: String) :
- this(listOf(packageName, *simpleNames)) {
- require(simpleNames.isNotEmpty()) { "simpleNames must not be empty" }
- require(simpleNames.none { it.isEmpty() }) {
- "simpleNames must not contain empty items: ${simpleNames.contentToString()}"
- }
- }
-
- /**
- * Returns a class name created from the given parts. For example, calling this with package name
- * `"java.util"` and simple names `"Map"`, `"Entry"` yields `Map.Entry`.
- */
- public constructor(packageName: String, simpleNames: List<String>) :
- this(mutableListOf(packageName).apply { addAll(simpleNames) }) {
- require(simpleNames.isNotEmpty()) { "simpleNames must not be empty" }
- require(simpleNames.none { it.isEmpty() }) {
- "simpleNames must not contain empty items: $simpleNames"
- }
- }
-
- /** From top to bottom. This will be `["java.util", "Map", "Entry"]` for `Map.Entry`. */
- private val names = names.toImmutableList()
-
- /** Fully qualified name using `.` as a separator, like `kotlin.collections.Map.Entry`. */
- public val canonicalName: String = if (names[0].isEmpty())
- names.subList(1, names.size).joinToString(".") else
- names.joinToString(".")
-
- /** Package name, like `"kotlin.collections"` for `Map.Entry`. */
- public val packageName: String get() = names[0]
-
- /** Simple name of this class, like `"Entry"` for `Map.Entry`. */
- public val simpleName: String get() = names[names.size - 1]
-
- /**
- * The enclosing classes, outermost first, followed by the simple name. This is `["Map", "Entry"]`
- * for `Map.Entry`.
- */
- public val simpleNames: List<String> get() = names.subList(1, names.size)
-
- override fun copy(
- nullable: Boolean,
- annotations: List<AnnotationSpec>,
- tags: Map<KClass<*>, Any>
- ): ClassName {
- return ClassName(names, nullable, annotations, tags)
- }
-
- /**
- * Returns the enclosing class, like `Map` for `Map.Entry`. Returns null if this class is not
- * nested in another class.
- */
- public fun enclosingClassName(): ClassName? {
- return if (names.size != 2)
- ClassName(names.subList(0, names.size - 1)) else
- null
- }
-
- /**
- * Returns the top class in this nesting group. Equivalent to chained calls to
- * [ClassName.enclosingClassName] until the result's enclosing class is null.
- */
- public fun topLevelClassName(): ClassName = ClassName(names.subList(0, 2))
-
- /**
- * Fully qualified name using `.` to separate package from the top level class name, and `$` to
- * separate nested classes, like `kotlin.collections.Map$Entry`.
- */
- public fun reflectionName(): String {
- // trivial case: no nested names
- if (names.size == 2) {
- return if (packageName.isEmpty())
- names[1] else
- packageName + "." + names[1]
- }
- // concat top level class name and nested names
- return buildString {
- append(topLevelClassName().canonicalName)
- for (name in simpleNames.subList(1, simpleNames.size)) {
- append('$').append(name)
- }
- }
- }
-
- /**
- * Callable reference to the constructor of this class. Emits the enclosing class if one exists,
- * followed by the reference operator `::`, followed by either [simpleName] or the
- * fully-qualified name if this is a top-level class.
- *
- * Note: As `::$packageName.$simpleName` is not valid syntax, an aliased import may be required
- * for a top-level class with a conflicting name.
- */
- public fun constructorReference(): CodeBlock {
- val enclosing = enclosingClassName()
- return if (enclosing != null) {
- CodeBlock.of("%T::%N", enclosing, simpleName)
- } else {
- CodeBlock.of("::%T", this)
- }
- }
-
- /** Returns a new [ClassName] instance for the specified `name` as nested inside this class. */
- public fun nestedClass(name: String): ClassName = ClassName(names + name)
-
- /**
- * Returns a class that shares the same enclosing package or class. If this class is enclosed by
- * another class, this is equivalent to `enclosingClassName().nestedClass(name)`. Otherwise
- * it is equivalent to `get(packageName(), name)`.
- */
- public fun peerClass(name: String): ClassName {
- val result = names.toMutableList()
- result[result.size - 1] = name
- return ClassName(result)
- }
-
- /**
- * Orders by the fully-qualified name. Nested types are ordered immediately after their
- * enclosing type. For example, the following types are ordered by this method:
- *
- * ```
- * com.example.Robot
- * com.example.Robot.Motor
- * com.example.RoboticVacuum
- * ```
- */
- override fun compareTo(other: ClassName): Int = canonicalName.compareTo(other.canonicalName)
-
- override fun emit(out: CodeWriter) =
- out.emit(out.lookupName(this).escapeSegmentsIfNecessary())
-
- public companion object {
- /**
- * Returns a new [ClassName] instance for the given fully-qualified class name string. This
- * method assumes that the input is ASCII and follows typical Java style (lowercase package
- * names, UpperCamelCase class names) and may produce incorrect results or throw
- * [IllegalArgumentException] otherwise. For that reason, the constructor should be preferred as
- * it can create [ClassName] instances without such restrictions.
- */
- @JvmStatic public fun bestGuess(classNameString: String): ClassName {
- val names = mutableListOf<String>()
-
- // Add the package name, like "java.util.concurrent", or "" for no package.
- var p = 0
- while (p < classNameString.length && Character.isLowerCase(classNameString.codePointAt(p))) {
- p = classNameString.indexOf('.', p) + 1
- require(p != 0) { "couldn't make a guess for $classNameString" }
- }
- names += if (p != 0) classNameString.substring(0, p - 1) else ""
-
- // Add the class names, like "Map" and "Entry".
- for (part in classNameString.substring(p).split('.')) {
- require(part.isNotEmpty() && Character.isUpperCase(part.codePointAt(0))) {
- "couldn't make a guess for $classNameString"
- }
-
- names += part
- }
-
- require(names.size >= 2) { "couldn't make a guess for $classNameString" }
- return ClassName(names)
- }
- }
-}
-
-public object Dynamic : TypeName(false, emptyList(), TagMap(emptyMap())) {
-
- override fun copy(
- nullable: Boolean,
- annotations: List<AnnotationSpec>,
- tags: Map<KClass<*>, Any>
- ): Nothing = throw UnsupportedOperationException("dynamic doesn't support copying")
-
- override fun emit(out: CodeWriter) = out.apply {
- emit("dynamic")
- }
-}
-
-public class LambdaTypeName private constructor(
- public val receiver: TypeName? = null,
- parameters: List<ParameterSpec> = emptyList(),
- public val returnType: TypeName = UNIT,
- nullable: Boolean = false,
- public val isSuspending: Boolean = false,
- annotations: List<AnnotationSpec> = emptyList(),
- tags: Map<KClass<*>, Any> = emptyMap()
-) : TypeName(nullable, annotations, TagMap(tags)) {
- public val parameters: List<ParameterSpec> = parameters.toImmutableList()
-
- init {
- for (param in parameters) {
- require(param.annotations.isEmpty()) { "Parameters with annotations are not allowed" }
- require(param.modifiers.isEmpty()) { "Parameters with modifiers are not allowed" }
- require(param.defaultValue == null) { "Parameters with default values are not allowed" }
- }
- }
-
- override fun copy(
- nullable: Boolean,
- annotations: List<AnnotationSpec>,
- tags: Map<KClass<*>, Any>
- ): LambdaTypeName {
- return copy(nullable, annotations, this.isSuspending, tags)
- }
-
- public fun copy(
- nullable: Boolean = this.isNullable,
- annotations: List<AnnotationSpec> = this.annotations.toList(),
- suspending: Boolean = this.isSuspending,
- tags: Map<KClass<*>, Any> = this.tags.toMap()
- ): LambdaTypeName {
- return LambdaTypeName(receiver, parameters, returnType, nullable, suspending, annotations, tags)
- }
-
- override fun emit(out: CodeWriter): CodeWriter {
- if (isNullable) {
- out.emit("(")
- }
-
- if (isSuspending) {
- out.emit("suspend ")
- }
-
- receiver?.let {
- if (it.isAnnotated) {
- out.emitCode("(%T).", it)
- } else {
- out.emitCode("%T.", it)
- }
- }
-
- parameters.emit(out)
- out.emitCode(if (returnType is LambdaTypeName) " -> (%T)" else " -> %T", returnType)
-
- if (isNullable) {
- out.emit(")")
- }
- return out
- }
-
- public companion object {
- /** Returns a lambda type with `returnType` and parameters listed in `parameters`. */
- @JvmStatic public fun get(
- receiver: TypeName? = null,
- parameters: List<ParameterSpec> = emptyList(),
- returnType: TypeName
- ): LambdaTypeName = LambdaTypeName(receiver, parameters, returnType)
-
- /** Returns a lambda type with `returnType` and parameters listed in `parameters`. */
- @JvmStatic public fun get(
- receiver: TypeName? = null,
- vararg parameters: TypeName = emptyArray(),
- returnType: TypeName
- ): LambdaTypeName {
- return LambdaTypeName(
- receiver,
- parameters.toList().map { ParameterSpec.unnamed(it) },
- returnType
- )
- }
-
- /** Returns a lambda type with `returnType` and parameters listed in `parameters`. */
- @JvmStatic public fun get(
- receiver: TypeName? = null,
- vararg parameters: ParameterSpec = emptyArray(),
- returnType: TypeName
- ): LambdaTypeName = LambdaTypeName(receiver, parameters.toList(), returnType)
- }
-}
-
-public class ParameterizedTypeName internal constructor(
- private val enclosingType: TypeName?,
- public val rawType: ClassName,
- typeArguments: List<TypeName>,
- nullable: Boolean = false,
- annotations: List<AnnotationSpec> = emptyList(),
- tags: Map<KClass<*>, Any> = emptyMap()
-) : TypeName(nullable, annotations, TagMap(tags)) {
- public val typeArguments: List<TypeName> = typeArguments.toImmutableList()
-
- init {
- require(typeArguments.isNotEmpty() || enclosingType != null) {
- "no type arguments: $rawType"
- }
- }
-
- override fun copy(
- nullable: Boolean,
- annotations: List<AnnotationSpec>,
- tags: Map<KClass<*>, Any>
- ): ParameterizedTypeName {
- return ParameterizedTypeName(enclosingType, rawType, typeArguments, nullable, annotations, tags)
- }
-
- public fun plusParameter(typeArgument: TypeName): ParameterizedTypeName =
- ParameterizedTypeName(
- enclosingType, rawType, typeArguments + typeArgument, isNullable,
- annotations
- )
-
- public fun plusParameter(typeArgument: KClass<*>): ParameterizedTypeName =
- plusParameter(typeArgument.asClassName())
-
- public fun plusParameter(typeArgument: Class<*>): ParameterizedTypeName =
- plusParameter(typeArgument.asClassName())
-
- override fun emit(out: CodeWriter): CodeWriter {
- if (enclosingType != null) {
- enclosingType.emitAnnotations(out)
- enclosingType.emit(out)
- out.emit("." + rawType.simpleName)
- } else {
- rawType.emitAnnotations(out)
- rawType.emit(out)
- }
- if (typeArguments.isNotEmpty()) {
- out.emit("<")
- typeArguments.forEachIndexed { index, parameter ->
- if (index > 0) out.emit(", ")
- parameter.emitAnnotations(out)
- parameter.emit(out)
- parameter.emitNullable(out)
- }
- out.emit(">")
- }
- return out
- }
-
- /**
- * Returns a new [ParameterizedTypeName] instance for the specified `name` as nested inside this
- * class, with the specified `typeArguments`.
- */
- public fun nestedClass(name: String, typeArguments: List<TypeName>): ParameterizedTypeName =
- ParameterizedTypeName(this, rawType.nestedClass(name), typeArguments)
-
- public companion object {
- /** Returns a parameterized type, applying `typeArguments` to `this`. */
- @JvmStatic @JvmName("get") public fun ClassName.parameterizedBy(
- vararg typeArguments: TypeName
- ): ParameterizedTypeName = ParameterizedTypeName(null, this, typeArguments.toList())
-
- /** Returns a parameterized type, applying `typeArguments` to `this`. */
- @JvmStatic @JvmName("get") public fun KClass<*>.parameterizedBy(
- vararg typeArguments: KClass<*>
- ): ParameterizedTypeName =
- ParameterizedTypeName(null, asClassName(), typeArguments.map { it.asTypeName() })
-
- /** Returns a parameterized type, applying `typeArguments` to `this`. */
- @JvmStatic @JvmName("get") public fun Class<*>.parameterizedBy(
- vararg typeArguments: Type
- ): ParameterizedTypeName =
- ParameterizedTypeName(null, asClassName(), typeArguments.map { it.asTypeName() })
-
- /** Returns a parameterized type, applying `typeArguments` to `this`. */
- @JvmStatic @JvmName("get") public fun ClassName.parameterizedBy(
- typeArguments: List<TypeName>
- ): ParameterizedTypeName = ParameterizedTypeName(null, this, typeArguments)
-
- /** Returns a parameterized type, applying `typeArguments` to `this`. */
- @JvmStatic @JvmName("get") public fun KClass<*>.parameterizedBy(
- typeArguments: Iterable<KClass<*>>
- ): ParameterizedTypeName =
- ParameterizedTypeName(null, asClassName(), typeArguments.map { it.asTypeName() })
-
- /** Returns a parameterized type, applying `typeArguments` to `this`. */
- @JvmStatic @JvmName("get") public fun Class<*>.parameterizedBy(
- typeArguments: Iterable<Type>
- ): ParameterizedTypeName =
- ParameterizedTypeName(null, asClassName(), typeArguments.map { it.asTypeName() })
-
- /** Returns a parameterized type, applying `typeArgument` to `this`. */
- @JvmStatic @JvmName("get") public fun ClassName.plusParameter(
- typeArgument: TypeName
- ): ParameterizedTypeName = parameterizedBy(typeArgument)
-
- /** Returns a parameterized type, applying `typeArgument` to `this`. */
- @JvmStatic @JvmName("get") public fun KClass<*>.plusParameter(
- typeArgument: KClass<*>
- ): ParameterizedTypeName = parameterizedBy(typeArgument)
-
- /** Returns a parameterized type, applying `typeArgument` to `this`. */
- @JvmStatic @JvmName("get") public fun Class<*>.plusParameter(
- typeArgument: Class<*>
- ): ParameterizedTypeName = parameterizedBy(typeArgument)
-
- /** Returns a parameterized type equivalent to `type`. */
- internal fun get(
- type: ParameterizedType,
- map: MutableMap<Type, TypeVariableName>
- ): ParameterizedTypeName {
- val rawType = (type.rawType as Class<*>).asClassName()
- val ownerType = if (type.ownerType is ParameterizedType &&
- !isStatic((type.rawType as Class<*>).modifiers)
- )
- type.ownerType as ParameterizedType else
- null
-
- val typeArguments = type.actualTypeArguments.map { get(it, map = map) }
- return if (ownerType != null)
- get(ownerType, map = map).nestedClass(rawType.simpleName, typeArguments) else
- ParameterizedTypeName(null, rawType, typeArguments)
- }
-
- /** Returns a type name equivalent to type with given list of type arguments. */
- internal fun get(
- type: KClass<*>,
- nullable: Boolean,
- typeArguments: List<KTypeProjection>
- ): TypeName {
- if (typeArguments.isEmpty()) {
- return type.asTypeName().run { if (nullable) copy(nullable = true) else this }
- }
-
- val effectiveType = if (type.java.isArray) Array<Unit>::class else type
- val enclosingClass = type.java.enclosingClass?.kotlin
-
- return ParameterizedTypeName(
- enclosingClass?.let {
- get(it, false, typeArguments.drop(effectiveType.typeParameters.size))
- },
- effectiveType.asTypeName(),
- typeArguments.take(effectiveType.typeParameters.size).map { (paramVariance, paramType) ->
- val typeName = paramType?.asTypeName() ?: return@map STAR
- when (paramVariance) {
- null -> STAR
- KVariance.INVARIANT -> typeName
- KVariance.IN -> WildcardTypeName.consumerOf(typeName)
- KVariance.OUT -> WildcardTypeName.producerOf(typeName)
- }
- },
- nullable,
- effectiveType.annotations.map { AnnotationSpec.get(it) }
- )
- }
- }
-}
-
-public class TypeVariableName private constructor(
- public val name: String,
- public val bounds: List<TypeName>,
-
- /** Either [KModifier.IN], [KModifier.OUT], or null. */
- public val variance: KModifier? = null,
- public val isReified: Boolean = false,
- nullable: Boolean = false,
- annotations: List<AnnotationSpec> = emptyList(),
- tags: Map<KClass<*>, Any> = emptyMap()
-) : TypeName(nullable, annotations, TagMap(tags)) {
-
- override fun copy(
- nullable: Boolean,
- annotations: List<AnnotationSpec>,
- tags: Map<KClass<*>, Any>
- ): TypeVariableName {
- return copy(nullable, annotations, this.bounds, this.isReified, tags)
- }
-
- public fun copy(
- nullable: Boolean = this.isNullable,
- annotations: List<AnnotationSpec> = this.annotations.toList(),
- bounds: List<TypeName> = this.bounds.toList(),
- reified: Boolean = this.isReified,
- tags: Map<KClass<*>, Any> = this.tagMap.tags
- ): TypeVariableName {
- return TypeVariableName(
- name, bounds.withoutImplicitBound(), variance, reified, nullable,
- annotations, tags
- )
- }
-
- private fun List<TypeName>.withoutImplicitBound(): List<TypeName> {
- return if (size == 1) this else filterNot { it == NULLABLE_ANY }
- }
-
- override fun emit(out: CodeWriter) = out.emit(name)
-
- public companion object {
- internal fun of(
- name: String,
- bounds: List<TypeName>,
- variance: KModifier?
- ): TypeVariableName {
- require(variance == null || variance.isOneOf(KModifier.IN, KModifier.OUT)) {
- "$variance is an invalid variance modifier, the only allowed values are in and out!"
- }
- require(bounds.isNotEmpty()) {
- "$name has no bounds"
- }
- // Strip Any? from bounds if it is present.
- return TypeVariableName(name, bounds, variance)
- }
-
- /** Returns type variable named `name` with `variance` and without bounds. */
- @JvmStatic @JvmName("get") @JvmOverloads
- public operator fun invoke(name: String, variance: KModifier? = null): TypeVariableName =
- of(name = name, bounds = NULLABLE_ANY_LIST, variance = variance)
-
- /** Returns type variable named `name` with `variance` and `bounds`. */
- @JvmStatic @JvmName("get") @JvmOverloads
- public operator fun invoke(
- name: String,
- vararg bounds: TypeName,
- variance: KModifier? = null
- ): TypeVariableName =
- of(
- name = name,
- bounds = bounds.toList().ifEmpty(::NULLABLE_ANY_LIST),
- variance = variance
- )
-
- /** Returns type variable named `name` with `variance` and `bounds`. */
- @JvmStatic @JvmName("get") @JvmOverloads
- public operator fun invoke(
- name: String,
- vararg bounds: KClass<*>,
- variance: KModifier? = null
- ): TypeVariableName =
- of(
- name = name,
- bounds = bounds.map(KClass<*>::asTypeName).ifEmpty(::NULLABLE_ANY_LIST),
- variance = variance
- )
-
- /** Returns type variable named `name` with `variance` and `bounds`. */
- @JvmStatic @JvmName("get") @JvmOverloads
- public operator fun invoke(
- name: String,
- vararg bounds: Type,
- variance: KModifier? = null
- ): TypeVariableName =
- of(
- name = name,
- bounds = bounds.map(Type::asTypeName).ifEmpty(::NULLABLE_ANY_LIST),
- variance = variance
- )
-
- /** Returns type variable named `name` with `variance` and `bounds`. */
- @JvmStatic @JvmName("get") @JvmOverloads
- public operator fun invoke(
- name: String,
- bounds: List<TypeName>,
- variance: KModifier? = null
- ): TypeVariableName = of(name, bounds.ifEmpty(::NULLABLE_ANY_LIST), variance)
-
- /** Returns type variable named `name` with `variance` and `bounds`. */
- @JvmStatic @JvmName("getWithClasses") @JvmOverloads
- public operator fun invoke(
- name: String,
- bounds: Iterable<KClass<*>>,
- variance: KModifier? = null
- ): TypeVariableName =
- of(
- name,
- bounds.map { it.asTypeName() }.ifEmpty(::NULLABLE_ANY_LIST),
- variance
- )
-
- /** Returns type variable named `name` with `variance` and `bounds`. */
- @JvmStatic @JvmName("getWithTypes") @JvmOverloads
- public operator fun invoke(
- name: String,
- bounds: Iterable<Type>,
- variance: KModifier? = null
- ): TypeVariableName =
- of(
- name,
- bounds.map { it.asTypeName() }.ifEmpty(::NULLABLE_ANY_LIST),
- variance
- )
-
- /**
- * Make a TypeVariableName for the given TypeMirror. This form is used internally to avoid
- * infinite recursion in cases like `Enum<E extends Enum<E>>`. When we encounter such a
- * thing, we will make a TypeVariableName without bounds and add that to the `typeVariables`
- * map before looking up the bounds. Then if we encounter this TypeVariable again while
- * constructing the bounds, we can just return it from the map. And, the code that put the entry
- * in `variables` will make sure that the bounds are filled in before returning.
- */
- internal fun get(
- mirror: javax.lang.model.type.TypeVariable,
- typeVariables: MutableMap<TypeParameterElement, TypeVariableName>
- ): TypeVariableName {
- val element = mirror.asElement() as TypeParameterElement
- var typeVariableName: TypeVariableName? = typeVariables[element]
- if (typeVariableName == null) {
- // Since the bounds field is public, we need to make it an unmodifiableList. But we control
- // the List that that wraps, which means we can change it before returning.
- val bounds = mutableListOf<TypeName>()
- val visibleBounds = Collections.unmodifiableList(bounds)
- typeVariableName = TypeVariableName(element.simpleName.toString(), visibleBounds)
- typeVariables[element] = typeVariableName
- for (typeMirror in element.bounds) {
- bounds += get(typeMirror, typeVariables)
- }
- bounds.remove(ANY)
- bounds.remove(JAVA_OBJECT)
- if (bounds.isEmpty()) {
- bounds.add(NULLABLE_ANY)
- }
- }
- return typeVariableName
- }
-
- /** Returns type variable equivalent to `type`. */
- internal fun get(
- type: TypeVariable<*>,
- map: MutableMap<Type, TypeVariableName> = mutableMapOf()
- ): TypeVariableName {
- var result: TypeVariableName? = map[type]
- if (result == null) {
- val bounds = mutableListOf<TypeName>()
- val visibleBounds = Collections.unmodifiableList(bounds)
- result = TypeVariableName(type.name, visibleBounds)
- map[type] = result
- for (bound in type.bounds) {
- bounds += get(bound, map)
- }
- bounds.remove(ANY)
- bounds.remove(JAVA_OBJECT)
- if (bounds.isEmpty()) {
- bounds.add(NULLABLE_ANY)
- }
- }
- return result
- }
-
- internal val NULLABLE_ANY_LIST = listOf(NULLABLE_ANY)
- private val JAVA_OBJECT = ClassName("java.lang", "Object")
- }
-}
-
-public class WildcardTypeName private constructor(
- outTypes: List<TypeName>,
- inTypes: List<TypeName>,
- nullable: Boolean = false,
- annotations: List<AnnotationSpec> = emptyList(),
- tags: Map<KClass<*>, Any> = emptyMap()
-) : TypeName(nullable, annotations, TagMap(tags)) {
- public val outTypes: List<TypeName> = outTypes.toImmutableList()
- public val inTypes: List<TypeName> = inTypes.toImmutableList()
-
- init {
- require(this.outTypes.size == 1) { "unexpected out types: $outTypes" }
- }
-
- override fun copy(
- nullable: Boolean,
- annotations: List<AnnotationSpec>,
- tags: Map<KClass<*>, Any>
- ): WildcardTypeName {
- return WildcardTypeName(outTypes, inTypes, nullable, annotations, tags)
- }
-
- override fun emit(out: CodeWriter): CodeWriter {
- return when {
- inTypes.size == 1 -> out.emitCode("in %T", inTypes[0])
- outTypes == STAR.outTypes -> out.emit("*")
- else -> out.emitCode("out %T", outTypes[0])
- }
- }
-
- public companion object {
- /**
- * Returns a type that represents an unknown type that produces `outType`. For example, if
- * `outType` is `CharSequence`, this returns `out CharSequence`. If `outType` is `Any?`, this
- * returns `*`, which is shorthand for `out Any?`.
- */
- @JvmStatic public fun producerOf(outType: TypeName): WildcardTypeName =
- WildcardTypeName(listOf(outType), emptyList())
-
- @JvmStatic public fun producerOf(outType: Type): WildcardTypeName =
- producerOf(outType.asTypeName())
-
- @JvmStatic public fun producerOf(outType: KClass<*>): WildcardTypeName =
- producerOf(outType.asTypeName())
-
- /**
- * Returns a type that represents an unknown type that consumes `inType`. For example, if
- * `inType` is `String`, this returns `in String`.
- */
- @JvmStatic public fun consumerOf(inType: TypeName): WildcardTypeName =
- WildcardTypeName(listOf(ANY), listOf(inType))
-
- @JvmStatic public fun consumerOf(inType: Type): WildcardTypeName =
- consumerOf(inType.asTypeName())
-
- @JvmStatic public fun consumerOf(inType: KClass<*>): WildcardTypeName =
- consumerOf(inType.asTypeName())
-
- internal fun get(
- mirror: javax.lang.model.type.WildcardType,
- typeVariables: Map<TypeParameterElement, TypeVariableName>
- ): TypeName {
- val outType = mirror.extendsBound
- if (outType == null) {
- val inType = mirror.superBound
- return if (inType == null) {
- STAR
- } else {
- consumerOf(get(inType, typeVariables))
- }
- } else {
- return producerOf(get(outType, typeVariables))
- }
- }
-
- internal fun get(
- wildcardName: WildcardType,
- map: MutableMap<Type, TypeVariableName>
- ): TypeName {
- return WildcardTypeName(
- wildcardName.upperBounds.map { get(it, map = map) },
- wildcardName.lowerBounds.map { get(it, map = map) }
- )
- }
- }
-}
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt
index cc8df36c..b3caeff6 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt
+++ b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt
@@ -17,13 +17,210 @@
package com.squareup.kotlinpoet
+import java.lang.reflect.Type
+import java.util.Collections
import javax.lang.model.element.TypeParameterElement
import javax.lang.model.type.TypeMirror
import javax.lang.model.type.TypeVariable
+import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.KTypeParameter
import kotlin.reflect.KVariance
+public class TypeVariableName private constructor(
+ public val name: String,
+ public val bounds: List<TypeName>,
+
+ /** Either [KModifier.IN], [KModifier.OUT], or null. */
+ public val variance: KModifier? = null,
+ public val isReified: Boolean = false,
+ nullable: Boolean = false,
+ annotations: List<AnnotationSpec> = emptyList(),
+ tags: Map<KClass<*>, Any> = emptyMap()
+) : TypeName(nullable, annotations, TagMap(tags)) {
+
+ override fun copy(
+ nullable: Boolean,
+ annotations: List<AnnotationSpec>,
+ tags: Map<KClass<*>, Any>
+ ): TypeVariableName {
+ return copy(nullable, annotations, this.bounds, this.isReified, tags)
+ }
+
+ public fun copy(
+ nullable: Boolean = this.isNullable,
+ annotations: List<AnnotationSpec> = this.annotations.toList(),
+ bounds: List<TypeName> = this.bounds.toList(),
+ reified: Boolean = this.isReified,
+ tags: Map<KClass<*>, Any> = this.tagMap.tags
+ ): TypeVariableName {
+ return TypeVariableName(
+ name, bounds.withoutImplicitBound(), variance, reified, nullable,
+ annotations, tags
+ )
+ }
+
+ private fun List<TypeName>.withoutImplicitBound(): List<TypeName> {
+ return if (size == 1) this else filterNot { it == NULLABLE_ANY }
+ }
+
+ override fun emit(out: CodeWriter) = out.emit(name)
+
+ public companion object {
+ internal fun of(
+ name: String,
+ bounds: List<TypeName>,
+ variance: KModifier?
+ ): TypeVariableName {
+ require(variance == null || variance.isOneOf(KModifier.IN, KModifier.OUT)) {
+ "$variance is an invalid variance modifier, the only allowed values are in and out!"
+ }
+ require(bounds.isNotEmpty()) {
+ "$name has no bounds"
+ }
+ // Strip Any? from bounds if it is present.
+ return TypeVariableName(name, bounds, variance)
+ }
+
+ /** Returns type variable named `name` with `variance` and without bounds. */
+ @JvmStatic @JvmName("get") @JvmOverloads
+ public operator fun invoke(name: String, variance: KModifier? = null): TypeVariableName =
+ of(name = name, bounds = NULLABLE_ANY_LIST, variance = variance)
+
+ /** Returns type variable named `name` with `variance` and `bounds`. */
+ @JvmStatic @JvmName("get") @JvmOverloads
+ public operator fun invoke(
+ name: String,
+ vararg bounds: TypeName,
+ variance: KModifier? = null
+ ): TypeVariableName =
+ of(
+ name = name,
+ bounds = bounds.toList().ifEmpty(::NULLABLE_ANY_LIST),
+ variance = variance
+ )
+
+ /** Returns type variable named `name` with `variance` and `bounds`. */
+ @JvmStatic @JvmName("get") @JvmOverloads
+ public operator fun invoke(
+ name: String,
+ vararg bounds: KClass<*>,
+ variance: KModifier? = null
+ ): TypeVariableName =
+ of(
+ name = name,
+ bounds = bounds.map(KClass<*>::asTypeName).ifEmpty(::NULLABLE_ANY_LIST),
+ variance = variance
+ )
+
+ /** Returns type variable named `name` with `variance` and `bounds`. */
+ @JvmStatic @JvmName("get") @JvmOverloads
+ public operator fun invoke(
+ name: String,
+ vararg bounds: Type,
+ variance: KModifier? = null
+ ): TypeVariableName =
+ of(
+ name = name,
+ bounds = bounds.map(Type::asTypeName).ifEmpty(::NULLABLE_ANY_LIST),
+ variance = variance
+ )
+
+ /** Returns type variable named `name` with `variance` and `bounds`. */
+ @JvmStatic @JvmName("get") @JvmOverloads
+ public operator fun invoke(
+ name: String,
+ bounds: List<TypeName>,
+ variance: KModifier? = null
+ ): TypeVariableName = of(name, bounds.ifEmpty(::NULLABLE_ANY_LIST), variance)
+
+ /** Returns type variable named `name` with `variance` and `bounds`. */
+ @JvmStatic @JvmName("getWithClasses") @JvmOverloads
+ public operator fun invoke(
+ name: String,
+ bounds: Iterable<KClass<*>>,
+ variance: KModifier? = null
+ ): TypeVariableName =
+ of(
+ name,
+ bounds.map { it.asTypeName() }.ifEmpty(::NULLABLE_ANY_LIST),
+ variance
+ )
+
+ /** Returns type variable named `name` with `variance` and `bounds`. */
+ @JvmStatic @JvmName("getWithTypes") @JvmOverloads
+ public operator fun invoke(
+ name: String,
+ bounds: Iterable<Type>,
+ variance: KModifier? = null
+ ): TypeVariableName =
+ of(
+ name,
+ bounds.map { it.asTypeName() }.ifEmpty(::NULLABLE_ANY_LIST),
+ variance
+ )
+
+ /**
+ * Make a TypeVariableName for the given TypeMirror. This form is used internally to avoid
+ * infinite recursion in cases like `Enum<E extends Enum<E>>`. When we encounter such a
+ * thing, we will make a TypeVariableName without bounds and add that to the `typeVariables`
+ * map before looking up the bounds. Then if we encounter this TypeVariable again while
+ * constructing the bounds, we can just return it from the map. And, the code that put the entry
+ * in `variables` will make sure that the bounds are filled in before returning.
+ */
+ internal fun get(
+ mirror: javax.lang.model.type.TypeVariable,
+ typeVariables: MutableMap<TypeParameterElement, TypeVariableName>
+ ): TypeVariableName {
+ val element = mirror.asElement() as TypeParameterElement
+ var typeVariableName: TypeVariableName? = typeVariables[element]
+ if (typeVariableName == null) {
+ // Since the bounds field is public, we need to make it an unmodifiableList. But we control
+ // the List that that wraps, which means we can change it before returning.
+ val bounds = mutableListOf<TypeName>()
+ val visibleBounds = Collections.unmodifiableList(bounds)
+ typeVariableName = TypeVariableName(element.simpleName.toString(), visibleBounds)
+ typeVariables[element] = typeVariableName
+ for (typeMirror in element.bounds) {
+ bounds += get(typeMirror, typeVariables)
+ }
+ bounds.remove(ANY)
+ bounds.remove(JAVA_OBJECT)
+ if (bounds.isEmpty()) {
+ bounds.add(NULLABLE_ANY)
+ }
+ }
+ return typeVariableName
+ }
+
+ /** Returns type variable equivalent to `type`. */
+ internal fun get(
+ type: java.lang.reflect.TypeVariable<*>,
+ map: MutableMap<Type, TypeVariableName> = mutableMapOf()
+ ): TypeVariableName {
+ var result: TypeVariableName? = map[type]
+ if (result == null) {
+ val bounds = mutableListOf<TypeName>()
+ val visibleBounds = Collections.unmodifiableList(bounds)
+ result = TypeVariableName(type.name, visibleBounds)
+ map[type] = result
+ for (bound in type.bounds) {
+ bounds += get(bound, map)
+ }
+ bounds.remove(ANY)
+ bounds.remove(JAVA_OBJECT)
+ if (bounds.isEmpty()) {
+ bounds.add(NULLABLE_ANY)
+ }
+ }
+ return result
+ }
+
+ internal val NULLABLE_ANY_LIST = listOf(NULLABLE_ANY)
+ private val JAVA_OBJECT = ClassName("java.lang", "Object")
+ }
+}
+
/** Returns type variable equivalent to `mirror`. */
@JvmName("get")
public fun TypeVariable.asTypeVariableName(): TypeVariableName =
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/WildcardTypeName.kt b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/WildcardTypeName.kt
index f9e5c50c..08c86532 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/WildcardTypeName.kt
+++ b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/WildcardTypeName.kt
@@ -17,8 +17,98 @@
package com.squareup.kotlinpoet
+import java.lang.reflect.Type
import java.lang.reflect.WildcardType
+import javax.lang.model.element.TypeParameterElement
import kotlin.DeprecationLevel.WARNING
+import kotlin.reflect.KClass
+
+public class WildcardTypeName private constructor(
+ outTypes: List<TypeName>,
+ inTypes: List<TypeName>,
+ nullable: Boolean = false,
+ annotations: List<AnnotationSpec> = emptyList(),
+ tags: Map<KClass<*>, Any> = emptyMap()
+) : TypeName(nullable, annotations, TagMap(tags)) {
+ public val outTypes: List<TypeName> = outTypes.toImmutableList()
+ public val inTypes: List<TypeName> = inTypes.toImmutableList()
+
+ init {
+ require(this.outTypes.size == 1) { "unexpected out types: $outTypes" }
+ }
+
+ override fun copy(
+ nullable: Boolean,
+ annotations: List<AnnotationSpec>,
+ tags: Map<KClass<*>, Any>
+ ): WildcardTypeName {
+ return WildcardTypeName(outTypes, inTypes, nullable, annotations, tags)
+ }
+
+ override fun emit(out: CodeWriter): CodeWriter {
+ return when {
+ inTypes.size == 1 -> out.emitCode("in %T", inTypes[0])
+ outTypes == STAR.outTypes -> out.emit("*")
+ else -> out.emitCode("out %T", outTypes[0])
+ }
+ }
+
+ public companion object {
+ /**
+ * Returns a type that represents an unknown type that produces `outType`. For example, if
+ * `outType` is `CharSequence`, this returns `out CharSequence`. If `outType` is `Any?`, this
+ * returns `*`, which is shorthand for `out Any?`.
+ */
+ @JvmStatic public fun producerOf(outType: TypeName): WildcardTypeName =
+ WildcardTypeName(listOf(outType), emptyList())
+
+ @JvmStatic public fun producerOf(outType: Type): WildcardTypeName =
+ producerOf(outType.asTypeName())
+
+ @JvmStatic public fun producerOf(outType: KClass<*>): WildcardTypeName =
+ producerOf(outType.asTypeName())
+
+ /**
+ * Returns a type that represents an unknown type that consumes `inType`. For example, if
+ * `inType` is `String`, this returns `in String`.
+ */
+ @JvmStatic public fun consumerOf(inType: TypeName): WildcardTypeName =
+ WildcardTypeName(listOf(ANY), listOf(inType))
+
+ @JvmStatic public fun consumerOf(inType: Type): WildcardTypeName =
+ consumerOf(inType.asTypeName())
+
+ @JvmStatic public fun consumerOf(inType: KClass<*>): WildcardTypeName =
+ consumerOf(inType.asTypeName())
+
+ internal fun get(
+ mirror: javax.lang.model.type.WildcardType,
+ typeVariables: Map<TypeParameterElement, TypeVariableName>
+ ): TypeName {
+ val outType = mirror.extendsBound
+ if (outType == null) {
+ val inType = mirror.superBound
+ return if (inType == null) {
+ STAR
+ } else {
+ consumerOf(get(inType, typeVariables))
+ }
+ } else {
+ return producerOf(get(outType, typeVariables))
+ }
+ }
+
+ internal fun get(
+ wildcardName: WildcardType,
+ map: MutableMap<Type, TypeVariableName>
+ ): TypeName {
+ return WildcardTypeName(
+ wildcardName.upperBounds.map { get(it, map = map) },
+ wildcardName.lowerBounds.map { get(it, map = map) }
+ )
+ }
+ }
+}
@Deprecated(
message = "Mirror APIs don't give complete information on Kotlin types. Consider using" +
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt
index 70e0e68b..1d9e2edb 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt
+++ b/kotlinpoet/src/main/java/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt
@@ -51,6 +51,10 @@ private fun jvmSuppressWildcardsAnnotation(suppress: Boolean = true) =
.apply { if (!suppress) addMember("suppress = false") }
.build()
+public fun TypeSpec.Builder.jvmInline(): TypeSpec.Builder = addAnnotation(JvmInline::class)
+
+public fun TypeSpec.Builder.jvmRecord(): TypeSpec.Builder = addAnnotation(JvmRecord::class)
+
public fun FunSpec.Builder.jvmStatic(): FunSpec.Builder = apply {
check(!name.isConstructor) { "Can't apply @JvmStatic to a constructor!" }
addAnnotation(JvmStatic::class)
@@ -121,7 +125,11 @@ public fun TypeName.jvmSuppressWildcards(suppress: Boolean = true): TypeName =
public fun TypeName.jvmWildcard(): TypeName =
copy(annotations = this.annotations + AnnotationSpec.builder(JvmWildcard::class).build())
+@Suppress("DEPRECATION")
+@Deprecated("'JvmDefault' is deprecated. Switch to new -Xjvm-default modes: `all` or `all-compatibility`")
public fun PropertySpec.Builder.jvmDefault(): PropertySpec.Builder =
addAnnotation(JvmDefault::class)
+@Suppress("DEPRECATION")
+@Deprecated("'JvmDefault' is deprecated. Switch to new -Xjvm-default modes: `all` or `all-compatibility`")
public fun FunSpec.Builder.jvmDefault(): FunSpec.Builder = addAnnotation(JvmDefault::class)
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotationSpecTest.kt b/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotationSpecTest.kt
index 11ec9283..b6a9114b 100644
--- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotationSpecTest.kt
+++ b/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotationSpecTest.kt
@@ -110,12 +110,12 @@ class AnnotationSpecTest {
|import kotlin.Float
|
|@AnnotationSpecTest.HasDefaultsAnnotation(
- | o = AnnotationSpecTest.Breakfast.PANCAKES,
- | p = 1701,
| f = 11.1,
- | m = [9, 8, 1],
- | l = Override::class,
| j = AnnotationSpecTest.AnnotationA(),
+ | l = Override::class,
+ | m = [9, 8, 1],
+ | o = AnnotationSpecTest.Breakfast.PANCAKES,
+ | p = 1701,
| q = AnnotationSpecTest.AnnotationC(value = "bar"),
| r = [Float::class, Double::class]
|)
@@ -140,12 +140,12 @@ class AnnotationSpecTest {
|import kotlin.Float
|
|@AnnotationSpecTest.HasDefaultsAnnotation(
- | o = AnnotationSpecTest.Breakfast.PANCAKES,
- | p = 1701,
| f = 11.1,
- | m = [9, 8, 1],
- | l = Override::class,
| j = AnnotationSpecTest.AnnotationA(),
+ | l = Override::class,
+ | m = [9, 8, 1],
+ | o = AnnotationSpecTest.Breakfast.PANCAKES,
+ | p = 1701,
| q = AnnotationSpecTest.AnnotationC(value = "bar"),
| r = [Float::class, Double::class]
|)
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt b/kotlinpoet/src/test/java/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt
index 241dfecb..99198ecd 100644
--- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt
+++ b/kotlinpoet/src/test/java/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt
@@ -20,9 +20,11 @@ import com.google.common.truth.Truth.assertThat
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.KModifier.DATA
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.PropertySpec
+import com.squareup.kotlinpoet.STRING
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.asTypeName
@@ -1106,4 +1108,71 @@ class JvmAnnotationsTest {
|""".trimMargin()
)
}
+
+ @Test fun jvmInlineClass() {
+ val file = FileSpec.builder("com.squareup.tacos", "Taco")
+ .addType(
+ TypeSpec.valueClassBuilder("Taco")
+ .jvmInline()
+ .primaryConstructor(
+ FunSpec.constructorBuilder()
+ .addParameter("value", STRING)
+ .build()
+ )
+ .addProperty(
+ PropertySpec.builder("value", STRING)
+ .initializer("value")
+ .build()
+ )
+ .build()
+ )
+ .build()
+ assertThat(file.toString()).isEqualTo(
+ """
+ |package com.squareup.tacos
+ |
+ |import kotlin.String
+ |import kotlin.jvm.JvmInline
+ |
+ |@JvmInline
+ |public value class Taco(
+ | public val `value`: String
+ |)
+ |""".trimMargin()
+ )
+ }
+
+ @Test fun jvmRecordClass() {
+ val file = FileSpec.builder("com.squareup.tacos", "Taco")
+ .addType(
+ TypeSpec.classBuilder("Taco")
+ .jvmRecord()
+ .addModifiers(DATA)
+ .primaryConstructor(
+ FunSpec.constructorBuilder()
+ .addParameter("value", STRING)
+ .build()
+ )
+ .addProperty(
+ PropertySpec.builder("value", STRING)
+ .initializer("value")
+ .build()
+ )
+ .build()
+ )
+ .build()
+ assertThat(file.toString()).isEqualTo(
+ """
+ |package com.squareup.tacos
+ |
+ |import kotlin.String
+ |import kotlin.jvm.JvmRecord
+ |
+ |@JvmRecord
+ |public data class Taco(
+ | public val `value`: String
+ |)
+ |""".trimMargin()
+ )
+ }
}