• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 Square, Inc.
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  * https://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 @file:JvmName("TypeNames")
17 
18 package com.squareup.kotlinpoet
19 
20 import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
21 import java.lang.reflect.GenericArrayType
22 import java.lang.reflect.ParameterizedType
23 import java.lang.reflect.Type
24 import java.lang.reflect.TypeVariable
25 import java.lang.reflect.WildcardType
26 import javax.lang.model.element.Modifier
27 import javax.lang.model.element.TypeElement
28 import javax.lang.model.element.TypeParameterElement
29 import javax.lang.model.type.ArrayType
30 import javax.lang.model.type.DeclaredType
31 import javax.lang.model.type.ErrorType
32 import javax.lang.model.type.NoType
33 import javax.lang.model.type.PrimitiveType
34 import javax.lang.model.type.TypeKind
35 import javax.lang.model.type.TypeMirror
36 import javax.lang.model.util.SimpleTypeVisitor7
37 import kotlin.reflect.KClass
38 import kotlin.reflect.typeOf
39 
40 /**
41  * Any type in Kotlin's type system. This class identifies simple types like `Int` and `String`,
42  * nullable types like `Int?`, composite types like `Array<String>` and `Set<String>`, and
43  * unassignable types like `Unit`.
44  *
45  * Type names are dumb identifiers only and do not model the values they name. For example, the
46  * type name for `kotlin.List` doesn't know about the `size()` function, the fact that lists are
47  * collections, or even that it accepts a single type parameter.
48  *
49  * Instances of this class are immutable value objects that implement `equals()` and `hashCode()`
50  * properly.
51  *
52  * Referencing existing types
53  * --------------------------
54  *
55  * In an annotation processor you can get a type name instance for a type mirror by calling
56  * [asTypeName]. In reflection code, you can use [asTypeName].
57 
58  * Defining new types
59  * ------------------
60  *
61  * Create new reference types like `com.example.HelloWorld` with [ClassName.bestGuess]. To build composite
62  * types like `Set<Long>`, use the factory methods on [ParameterizedTypeName], [TypeVariableName],
63  * and [WildcardTypeName].
64  */
65 public sealed class TypeName constructor(
66   public val isNullable: Boolean,
67   annotations: List<AnnotationSpec>,
68   internal val tagMap: TagMap,
<lambda>null69 ) : Taggable by tagMap {
70   public val annotations: List<AnnotationSpec> = annotations.toImmutableList()
71 
72   /** Lazily-initialized toString of this type name.  */
73   private val cachedString: String by lazy {
74     buildCodeString {
75       emitAnnotations(this)
76       emit(this)
77       if (isNullable) emit("?")
78     }
79   }
80 
81   public fun copy(
82     nullable: Boolean = this.isNullable,
83     annotations: List<AnnotationSpec> = this.annotations.toList(),
84   ): TypeName {
85     return copy(nullable, annotations, this.tags)
86   }
87 
88   public abstract fun copy(
89     nullable: Boolean = this.isNullable,
90     annotations: List<AnnotationSpec> = this.annotations.toList(),
91     tags: Map<KClass<*>, Any> = this.tags,
92   ): TypeName
93 
94   public val isAnnotated: Boolean get() = annotations.isNotEmpty()
95 
96   override fun equals(other: Any?): Boolean {
97     if (this === other) return true
98     if (other == null) return false
99     if (javaClass != other.javaClass) return false
100     return toString() == other.toString()
101   }
102 
103   override fun hashCode(): Int = toString().hashCode()
104 
105   override fun toString(): String = cachedString
106 
107   internal abstract fun emit(out: CodeWriter): CodeWriter
108 
109   internal fun emitAnnotations(out: CodeWriter) {
110     for (annotation in annotations) {
111       annotation.emit(out, true)
112       out.emit(" ")
113     }
114   }
115 
116   internal fun emitNullable(out: CodeWriter) {
117     if (isNullable) {
118       out.emit("?")
119     }
120   }
121 
122   public companion object {
123     internal fun get(
124       mirror: TypeMirror,
125       typeVariables: Map<TypeParameterElement, TypeVariableName>,
126     ): TypeName {
127       return mirror.accept(
128         object : SimpleTypeVisitor7<TypeName, Void?>() {
129           override fun visitPrimitive(t: PrimitiveType, p: Void?): TypeName {
130             return when (t.kind) {
131               TypeKind.BOOLEAN -> BOOLEAN
132               TypeKind.BYTE -> BYTE
133               TypeKind.SHORT -> SHORT
134               TypeKind.INT -> INT
135               TypeKind.LONG -> LONG
136               TypeKind.CHAR -> CHAR
137               TypeKind.FLOAT -> FLOAT
138               TypeKind.DOUBLE -> DOUBLE
139               else -> throw AssertionError()
140             }
141           }
142 
143           override fun visitDeclared(t: DeclaredType, p: Void?): TypeName {
144             val rawType: ClassName = (t.asElement() as TypeElement).asClassName()
145             val enclosingType = t.enclosingType
146             val enclosing = if (enclosingType.kind != TypeKind.NONE &&
147               Modifier.STATIC !in t.asElement().modifiers
148             ) {
149               enclosingType.accept(this, null)
150             } else {
151               null
152             }
153             if (t.typeArguments.isEmpty() && enclosing !is ParameterizedTypeName) {
154               return rawType
155             }
156 
157             val typeArgumentNames = mutableListOf<TypeName>()
158             for (typeArgument in t.typeArguments) {
159               typeArgumentNames += get(typeArgument, typeVariables)
160             }
161             return if (enclosing is ParameterizedTypeName) {
162               enclosing.nestedClass(rawType.simpleName, typeArgumentNames)
163             } else {
164               ParameterizedTypeName(null, rawType, typeArgumentNames)
165             }
166           }
167 
168           override fun visitError(t: ErrorType, p: Void?): TypeName {
169             return visitDeclared(t, p)
170           }
171 
172           override fun visitArray(t: ArrayType, p: Void?): ParameterizedTypeName {
173             return ARRAY.parameterizedBy(get(t.componentType, typeVariables))
174           }
175 
176           override fun visitTypeVariable(
177             t: javax.lang.model.type.TypeVariable,
178             p: Void?,
179           ): TypeName {
180             return TypeVariableName.get(t, typeVariables.toMutableMap())
181           }
182 
183           override fun visitWildcard(t: javax.lang.model.type.WildcardType, p: Void?): TypeName {
184             return WildcardTypeName.get(t, typeVariables)
185           }
186 
187           override fun visitNoType(t: NoType, p: Void?): TypeName {
188             if (t.kind == TypeKind.VOID) return UNIT
189             return super.visitUnknown(t, p)
190           }
191 
192           override fun defaultAction(e: TypeMirror?, p: Void?): TypeName {
193             throw IllegalArgumentException("Unexpected type mirror: " + e!!)
194           }
195         },
196         null,
197       )
198     }
199 
200     internal fun get(type: Type, map: MutableMap<Type, TypeVariableName>): TypeName {
201       return when (type) {
202         is Class<*> -> when {
203           type === Void.TYPE -> UNIT
204           type === Boolean::class.javaPrimitiveType -> BOOLEAN
205           type === Byte::class.javaPrimitiveType -> BYTE
206           type === Short::class.javaPrimitiveType -> SHORT
207           type === Int::class.javaPrimitiveType -> INT
208           type === Long::class.javaPrimitiveType -> LONG
209           type === Char::class.javaPrimitiveType -> CHAR
210           type === Float::class.javaPrimitiveType -> FLOAT
211           type === Double::class.javaPrimitiveType -> DOUBLE
212           type.isArray -> ARRAY.parameterizedBy(get(type.componentType, map))
213           else -> type.asClassName()
214         }
215         is ParameterizedType -> ParameterizedTypeName.get(type, map)
216         is WildcardType -> WildcardTypeName.get(type, map)
217         is TypeVariable<*> -> TypeVariableName.get(type, map)
218         is GenericArrayType -> ARRAY.parameterizedBy(get(type.genericComponentType, map))
219         else -> throw IllegalArgumentException("unexpected type: $type")
220       }
221     }
222   }
223 }
224 
225 @JvmField public val ANY: ClassName = ClassName("kotlin", "Any")
226 
227 @JvmField public val ARRAY: ClassName = ClassName("kotlin", "Array")
228 
229 @JvmField public val UNIT: ClassName = ClassName("kotlin", "Unit")
230 
231 @JvmField public val BOOLEAN: ClassName = ClassName("kotlin", "Boolean")
232 
233 @JvmField public val BYTE: ClassName = ClassName("kotlin", "Byte")
234 
235 @JvmField public val SHORT: ClassName = ClassName("kotlin", "Short")
236 
237 @JvmField public val INT: ClassName = ClassName("kotlin", "Int")
238 
239 @JvmField public val LONG: ClassName = ClassName("kotlin", "Long")
240 
241 @JvmField public val CHAR: ClassName = ClassName("kotlin", "Char")
242 
243 @JvmField public val FLOAT: ClassName = ClassName("kotlin", "Float")
244 
245 @JvmField public val DOUBLE: ClassName = ClassName("kotlin", "Double")
246 
247 @JvmField public val STRING: ClassName = ClassName("kotlin", "String")
248 
249 @JvmField public val CHAR_SEQUENCE: ClassName = ClassName("kotlin", "CharSequence")
250 
251 @JvmField public val COMPARABLE: ClassName = ClassName("kotlin", "Comparable")
252 
253 @JvmField public val THROWABLE: ClassName = ClassName("kotlin", "Throwable")
254 
255 @JvmField public val ANNOTATION: ClassName = ClassName("kotlin", "Annotation")
256 
257 @JvmField public val NOTHING: ClassName = ClassName("kotlin", "Nothing")
258 
259 @JvmField public val NUMBER: ClassName = ClassName("kotlin", "Number")
260 
261 @JvmField public val ITERABLE: ClassName = ClassName("kotlin.collections", "Iterable")
262 
263 @JvmField public val COLLECTION: ClassName = ClassName("kotlin.collections", "Collection")
264 
265 @JvmField public val LIST: ClassName = ClassName("kotlin.collections", "List")
266 
267 @JvmField public val SET: ClassName = ClassName("kotlin.collections", "Set")
268 
269 @JvmField public val MAP: ClassName = ClassName("kotlin.collections", "Map")
270 
271 @JvmField public val MAP_ENTRY: ClassName = MAP.nestedClass("Entry")
272 
273 @JvmField public val MUTABLE_ITERABLE: ClassName =
274   ClassName("kotlin.collections", "MutableIterable")
275 
276 @JvmField public val MUTABLE_COLLECTION: ClassName =
277   ClassName("kotlin.collections", "MutableCollection")
278 
279 @JvmField public val MUTABLE_LIST: ClassName = ClassName("kotlin.collections", "MutableList")
280 
281 @JvmField public val MUTABLE_SET: ClassName = ClassName("kotlin.collections", "MutableSet")
282 
283 @JvmField public val MUTABLE_MAP: ClassName = ClassName("kotlin.collections", "MutableMap")
284 
285 @JvmField public val MUTABLE_MAP_ENTRY: ClassName = MUTABLE_MAP.nestedClass("Entry")
286 
287 @JvmField public val BOOLEAN_ARRAY: ClassName = ClassName("kotlin", "BooleanArray")
288 
289 @JvmField public val BYTE_ARRAY: ClassName = ClassName("kotlin", "ByteArray")
290 
291 @JvmField public val CHAR_ARRAY: ClassName = ClassName("kotlin", "CharArray")
292 
293 @JvmField public val SHORT_ARRAY: ClassName = ClassName("kotlin", "ShortArray")
294 
295 @JvmField public val INT_ARRAY: ClassName = ClassName("kotlin", "IntArray")
296 
297 @JvmField public val LONG_ARRAY: ClassName = ClassName("kotlin", "LongArray")
298 
299 @JvmField public val FLOAT_ARRAY: ClassName = ClassName("kotlin", "FloatArray")
300 
301 @JvmField public val DOUBLE_ARRAY: ClassName = ClassName("kotlin", "DoubleArray")
302 
303 @JvmField public val ENUM: ClassName = ClassName("kotlin", "Enum")
304 
305 @JvmField public val U_BYTE: ClassName = ClassName("kotlin", "UByte")
306 
307 @JvmField public val U_SHORT: ClassName = ClassName("kotlin", "UShort")
308 
309 @JvmField public val U_INT: ClassName = ClassName("kotlin", "UInt")
310 
311 @JvmField public val U_LONG: ClassName = ClassName("kotlin", "ULong")
312 
313 @JvmField public val U_BYTE_ARRAY: ClassName = ClassName("kotlin", "UByteArray")
314 
315 @JvmField public val U_SHORT_ARRAY: ClassName = ClassName("kotlin", "UShortArray")
316 
317 @JvmField public val U_INT_ARRAY: ClassName = ClassName("kotlin", "UIntArray")
318 
319 @JvmField public val U_LONG_ARRAY: ClassName = ClassName("kotlin", "ULongArray")
320 
321 /** The wildcard type `*` which is shorthand for `out Any?`. */
322 @JvmField public val STAR: WildcardTypeName = WildcardTypeName.producerOf(ANY.copy(nullable = true))
323 
324 /** [Dynamic] is a singleton `object` type, so this is a shorthand for it in Java. */
325 @JvmField public val DYNAMIC: Dynamic = Dynamic
326 
327 /** Returns a [TypeName] equivalent to this [TypeMirror]. */
328 @DelicateKotlinPoetApi(
329   message = "Mirror APIs don't give complete information on Kotlin types. Consider using" +
330     " the kotlinpoet-metadata APIs instead.",
331 )
332 @JvmName("get")
asTypeNamenull333 public fun TypeMirror.asTypeName(): TypeName = TypeName.get(this, mutableMapOf())
334 
335 /** Returns a [TypeName] equivalent to this [KClass].  */
336 @JvmName("get")
337 public fun KClass<*>.asTypeName(): ClassName = asClassName()
338 
339 /** Returns a [TypeName] equivalent to this [Type].  */
340 @JvmName("get")
341 public fun Type.asTypeName(): TypeName = TypeName.get(this, mutableMapOf())
342 
343 /**
344  * Returns a [TypeName] equivalent of the reified type parameter [T] using reflection, maybe using kotlin-reflect
345  * if required.
346  */
347 public inline fun <reified T> typeNameOf(): TypeName = typeOf<T>().asTypeName()
348