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.SimpleTypeVisitor8
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,
69 ) : Taggable by tagMap, Annotatable {
70 override val annotations: List<AnnotationSpec> = annotations.toImmutableList()
71
72 /** Lazily-initialized toString of this type name. */
<lambda>null73 private val cachedString: String by lazy {
74 buildCodeString {
75 emitAnnotations(this)
76 emit(this)
77 if (isNullable) emit("?")
78 }
79 }
80
copynull81 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
copynull88 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 (javaClass != other?.javaClass) return false
99
100 other as TypeName
101
102 if (isNullable != other.isNullable) return false
103 if (annotations != other.annotations) return false
104 // do not check for equality of tags, these are considered side-channel data
105
106 return true
107 }
108
hashCodenull109 override fun hashCode(): Int {
110 var result = isNullable.hashCode()
111 result = 31 * result + annotations.hashCode()
112 return result
113 }
114
toStringnull115 override fun toString(): String = cachedString
116
117 internal abstract fun emit(out: CodeWriter): CodeWriter
118
119 internal fun emitAnnotations(out: CodeWriter) {
120 for (annotation in annotations) {
121 annotation.emit(out, true)
122 out.emit(" ")
123 }
124 }
125
emitNullablenull126 internal fun emitNullable(out: CodeWriter) {
127 if (isNullable) {
128 out.emit("?")
129 }
130 }
131
132 public companion object {
getnull133 internal fun get(
134 mirror: TypeMirror,
135 typeVariables: Map<TypeParameterElement, TypeVariableName>,
136 ): TypeName {
137 return mirror.accept(
138 object : SimpleTypeVisitor8<TypeName, Void?>() {
139 override fun visitPrimitive(t: PrimitiveType, p: Void?): TypeName {
140 return when (t.kind) {
141 TypeKind.BOOLEAN -> BOOLEAN
142 TypeKind.BYTE -> BYTE
143 TypeKind.SHORT -> SHORT
144 TypeKind.INT -> INT
145 TypeKind.LONG -> LONG
146 TypeKind.CHAR -> CHAR
147 TypeKind.FLOAT -> FLOAT
148 TypeKind.DOUBLE -> DOUBLE
149 else -> throw AssertionError()
150 }
151 }
152
153 override fun visitDeclared(t: DeclaredType, p: Void?): TypeName {
154 val rawType: ClassName = (t.asElement() as TypeElement).asClassName()
155 val enclosingType = t.enclosingType
156 val enclosing = if (enclosingType.kind != TypeKind.NONE &&
157 Modifier.STATIC !in t.asElement().modifiers
158 ) {
159 enclosingType.accept(this, null)
160 } else {
161 null
162 }
163 if (t.typeArguments.isEmpty() && enclosing !is ParameterizedTypeName) {
164 return rawType
165 }
166
167 val typeArgumentNames = mutableListOf<TypeName>()
168 for (typeArgument in t.typeArguments) {
169 typeArgumentNames += get(typeArgument, typeVariables)
170 }
171 return if (enclosing is ParameterizedTypeName) {
172 enclosing.nestedClass(rawType.simpleName, typeArgumentNames)
173 } else {
174 ParameterizedTypeName(null, rawType, typeArgumentNames)
175 }
176 }
177
178 override fun visitError(t: ErrorType, p: Void?): TypeName {
179 return visitDeclared(t, p)
180 }
181
182 override fun visitArray(t: ArrayType, p: Void?): ParameterizedTypeName {
183 return ARRAY.parameterizedBy(get(t.componentType, typeVariables))
184 }
185
186 override fun visitTypeVariable(
187 t: javax.lang.model.type.TypeVariable,
188 p: Void?,
189 ): TypeName {
190 return TypeVariableName.get(t, typeVariables.toMutableMap())
191 }
192
193 override fun visitWildcard(t: javax.lang.model.type.WildcardType, p: Void?): TypeName {
194 return WildcardTypeName.get(t, typeVariables)
195 }
196
197 override fun visitNoType(t: NoType, p: Void?): TypeName {
198 if (t.kind == TypeKind.VOID) return UNIT
199 return super.visitUnknown(t, p)
200 }
201
202 override fun defaultAction(e: TypeMirror?, p: Void?): TypeName {
203 throw IllegalArgumentException("Unexpected type mirror: " + e!!)
204 }
205 },
206 null,
207 )
208 }
209
getnull210 internal fun get(type: Type, map: MutableMap<Type, TypeVariableName>): TypeName {
211 return when (type) {
212 is Class<*> -> when {
213 type === Void.TYPE -> UNIT
214 type === Boolean::class.javaPrimitiveType -> BOOLEAN
215 type === Byte::class.javaPrimitiveType -> BYTE
216 type === Short::class.javaPrimitiveType -> SHORT
217 type === Int::class.javaPrimitiveType -> INT
218 type === Long::class.javaPrimitiveType -> LONG
219 type === Char::class.javaPrimitiveType -> CHAR
220 type === Float::class.javaPrimitiveType -> FLOAT
221 type === Double::class.javaPrimitiveType -> DOUBLE
222 type.isArray -> ARRAY.parameterizedBy(get(type.componentType, map))
223 else -> type.asClassName()
224 }
225 is ParameterizedType -> ParameterizedTypeName.get(type, map)
226 is WildcardType -> WildcardTypeName.get(type, map)
227 is TypeVariable<*> -> TypeVariableName.get(type, map)
228 is GenericArrayType -> ARRAY.parameterizedBy(get(type.genericComponentType, map))
229 else -> throw IllegalArgumentException("unexpected type: $type")
230 }
231 }
232 }
233 }
234
235 @JvmField public val ANY: ClassName = ClassName("kotlin", "Any")
236
237 @JvmField public val ARRAY: ClassName = ClassName("kotlin", "Array")
238
239 @JvmField public val UNIT: ClassName = ClassName("kotlin", "Unit")
240
241 @JvmField public val BOOLEAN: ClassName = ClassName("kotlin", "Boolean")
242
243 @JvmField public val BYTE: ClassName = ClassName("kotlin", "Byte")
244
245 @JvmField public val SHORT: ClassName = ClassName("kotlin", "Short")
246
247 @JvmField public val INT: ClassName = ClassName("kotlin", "Int")
248
249 @JvmField public val LONG: ClassName = ClassName("kotlin", "Long")
250
251 @JvmField public val CHAR: ClassName = ClassName("kotlin", "Char")
252
253 @JvmField public val FLOAT: ClassName = ClassName("kotlin", "Float")
254
255 @JvmField public val DOUBLE: ClassName = ClassName("kotlin", "Double")
256
257 @JvmField public val STRING: ClassName = ClassName("kotlin", "String")
258
259 @JvmField public val CHAR_SEQUENCE: ClassName = ClassName("kotlin", "CharSequence")
260
261 @JvmField public val COMPARABLE: ClassName = ClassName("kotlin", "Comparable")
262
263 @JvmField public val THROWABLE: ClassName = ClassName("kotlin", "Throwable")
264
265 @JvmField public val ANNOTATION: ClassName = ClassName("kotlin", "Annotation")
266
267 @JvmField public val NOTHING: ClassName = ClassName("kotlin", "Nothing")
268
269 @JvmField public val NUMBER: ClassName = ClassName("kotlin", "Number")
270
271 @JvmField public val ITERABLE: ClassName = ClassName("kotlin.collections", "Iterable")
272
273 @JvmField public val COLLECTION: ClassName = ClassName("kotlin.collections", "Collection")
274
275 @JvmField public val LIST: ClassName = ClassName("kotlin.collections", "List")
276
277 @JvmField public val SET: ClassName = ClassName("kotlin.collections", "Set")
278
279 @JvmField public val MAP: ClassName = ClassName("kotlin.collections", "Map")
280
281 @JvmField public val MAP_ENTRY: ClassName = MAP.nestedClass("Entry")
282
283 @JvmField public val MUTABLE_ITERABLE: ClassName =
284 ClassName("kotlin.collections", "MutableIterable")
285
286 @JvmField public val MUTABLE_COLLECTION: ClassName =
287 ClassName("kotlin.collections", "MutableCollection")
288
289 @JvmField public val MUTABLE_LIST: ClassName = ClassName("kotlin.collections", "MutableList")
290
291 @JvmField public val MUTABLE_SET: ClassName = ClassName("kotlin.collections", "MutableSet")
292
293 @JvmField public val MUTABLE_MAP: ClassName = ClassName("kotlin.collections", "MutableMap")
294
295 @JvmField public val MUTABLE_MAP_ENTRY: ClassName = MUTABLE_MAP.nestedClass("Entry")
296
297 @JvmField public val BOOLEAN_ARRAY: ClassName = ClassName("kotlin", "BooleanArray")
298
299 @JvmField public val BYTE_ARRAY: ClassName = ClassName("kotlin", "ByteArray")
300
301 @JvmField public val CHAR_ARRAY: ClassName = ClassName("kotlin", "CharArray")
302
303 @JvmField public val SHORT_ARRAY: ClassName = ClassName("kotlin", "ShortArray")
304
305 @JvmField public val INT_ARRAY: ClassName = ClassName("kotlin", "IntArray")
306
307 @JvmField public val LONG_ARRAY: ClassName = ClassName("kotlin", "LongArray")
308
309 @JvmField public val FLOAT_ARRAY: ClassName = ClassName("kotlin", "FloatArray")
310
311 @JvmField public val DOUBLE_ARRAY: ClassName = ClassName("kotlin", "DoubleArray")
312
313 @JvmField public val ENUM: ClassName = ClassName("kotlin", "Enum")
314
315 @JvmField public val U_BYTE: ClassName = ClassName("kotlin", "UByte")
316
317 @JvmField public val U_SHORT: ClassName = ClassName("kotlin", "UShort")
318
319 @JvmField public val U_INT: ClassName = ClassName("kotlin", "UInt")
320
321 @JvmField public val U_LONG: ClassName = ClassName("kotlin", "ULong")
322
323 @JvmField public val U_BYTE_ARRAY: ClassName = ClassName("kotlin", "UByteArray")
324
325 @JvmField public val U_SHORT_ARRAY: ClassName = ClassName("kotlin", "UShortArray")
326
327 @JvmField public val U_INT_ARRAY: ClassName = ClassName("kotlin", "UIntArray")
328
329 @JvmField public val U_LONG_ARRAY: ClassName = ClassName("kotlin", "ULongArray")
330
331 /** The wildcard type `*` which is shorthand for `out Any?`. */
332 @JvmField public val STAR: WildcardTypeName = WildcardTypeName.producerOf(ANY.copy(nullable = true))
333
334 /** [Dynamic] is a singleton `object` type, so this is a shorthand for it in Java. */
335 @JvmField public val DYNAMIC: Dynamic = Dynamic
336
337 /** Returns a [TypeName] equivalent to this [TypeMirror]. */
338 @DelicateKotlinPoetApi(
339 message = "Mirror APIs don't give complete information on Kotlin types. Consider using" +
340 " the kotlinpoet-metadata APIs instead.",
341 )
342 @JvmName("get")
asTypeNamenull343 public fun TypeMirror.asTypeName(): TypeName = TypeName.get(this, mutableMapOf())
344
345 /** Returns a [TypeName] equivalent to this [KClass]. */
346 @JvmName("get")
347 public fun KClass<*>.asTypeName(): ClassName = asClassName()
348
349 /** Returns a [TypeName] equivalent to this [Type]. */
350 @JvmName("get")
351 public fun Type.asTypeName(): TypeName = TypeName.get(this, mutableMapOf())
352
353 /**
354 * Returns a [TypeName] equivalent of the reified type parameter [T] using reflection, maybe using kotlin-reflect
355 * if required.
356 */
357 public inline fun <reified T> typeNameOf(): TypeName = typeOf<T>().asTypeName()
358