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