blob: c3414c228988fb4120b8132c43d2e67fc7e7d183 [file] [log] [blame]
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.kotlinpoet
import java.io.IOException
import java.lang.reflect.GenericArrayType
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.lang.reflect.TypeVariable
import java.lang.reflect.WildcardType
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
import javax.lang.model.element.TypeParameterElement
import javax.lang.model.type.ArrayType
import javax.lang.model.type.DeclaredType
import javax.lang.model.type.ErrorType
import javax.lang.model.type.NoType
import javax.lang.model.type.PrimitiveType
import javax.lang.model.type.TypeKind
import javax.lang.model.type.TypeMirror
import javax.lang.model.util.SimpleTypeVisitor7
import kotlin.reflect.KClass
/**
* Any type in Kotlin's type system. This class identifies simple types like `Int` and `String`,
* nullable types like `Int?`, composite types like `Array<String>` and `Set<String>`, and
* unassignable types like `Unit`.
*
* Type names are dumb identifiers only and do not model the values they name. For example, the
* type name for `kotlin.List` doesn't know about the `size()` function, the fact that lists are
* collections, or even that it accepts a single type parameter.
*
* Instances of this class are immutable value objects that implement `equals()` and `hashCode()`
* properly.
*
* Referencing existing types
* --------------------------
*
* In an annotation processor you can get a type name instance for a type mirror by calling
* [TypeName.get]. In reflection code, you can use [TypeName.get].
* Defining new types
* ------------------
*
* Create new reference types like `com.example.HelloWorld` with [ClassName.get]. To build composite
* types like `Set<Long>`, use the factory methods on [ParameterizedTypeName], [TypeVariableName],
* and [WildcardTypeName].
*/
abstract class TypeName internal constructor(annotations: List<AnnotationSpec>) {
val annotations: List<AnnotationSpec> = Util.immutableList(annotations)
/** Lazily-initialized toString of this type name. */
val cachedString: String by lazy {
val resultBuilder = StringBuilder()
val codeWriter = CodeWriter(resultBuilder)
emitAnnotations(codeWriter)
abstractEmit(codeWriter)
resultBuilder.toString()
}
fun annotated(vararg annotations: AnnotationSpec): TypeName {
return annotated(annotations.toList())
}
abstract fun annotated(annotations: List<AnnotationSpec>): TypeName
abstract fun withoutAnnotations(): TypeName
val isAnnotated: Boolean
get() = !annotations.isEmpty()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null) return false
if (javaClass != other.javaClass) return false
return toString() == other.toString()
}
override fun hashCode(): Int {
return toString().hashCode()
}
override fun toString(): String {
return cachedString
}
@Throws(IOException::class)
internal abstract fun abstractEmit(out: CodeWriter): CodeWriter
// TODO(jwilson): once the calling site is Kotlin, rename abstractEmit to emit.
@JvmName("emit") internal fun emit(out: CodeWriter) {
abstractEmit(out)
}
@Throws(IOException::class)
@JvmName("emitAnnotations") internal fun emitAnnotations(out: CodeWriter) {
for (annotation in annotations) {
annotation.emit(out, true)
out.emit(" ")
}
}
companion object {
/** Returns a type name equivalent to `mirror`. */
@JvmOverloads @JvmStatic fun get(
mirror: TypeMirror,
typeVariables: MutableMap<TypeParameterElement, TypeVariableName> = mutableMapOf())
: TypeName {
return mirror.accept(object : SimpleTypeVisitor7<TypeName, Void?>() {
override fun visitPrimitive(t: PrimitiveType, p: Void?): TypeName {
return when (t.kind) {
TypeKind.BOOLEAN -> BOOLEAN
TypeKind.BYTE -> BYTE
TypeKind.SHORT -> SHORT
TypeKind.INT -> INT
TypeKind.LONG -> LONG
TypeKind.CHAR -> CHAR
TypeKind.FLOAT -> FLOAT
TypeKind.DOUBLE -> DOUBLE
else -> throw AssertionError()
}
}
override fun visitDeclared(t: DeclaredType, p: Void?): TypeName {
val rawType : ClassName = ClassName.get(t.asElement() as TypeElement)
val enclosingType = t.enclosingType
val enclosing = if (enclosingType.kind != TypeKind.NONE
&& !t.asElement().modifiers.contains(Modifier.STATIC))
enclosingType.accept(this, null) else
null
if (t.typeArguments.isEmpty() && enclosing !is ParameterizedTypeName) {
return rawType
}
val typeArgumentNames = mutableListOf<TypeName>()
for (typeArgument in t.typeArguments) {
typeArgumentNames.add(get(typeArgument, typeVariables))
}
return if (enclosing is ParameterizedTypeName)
enclosing.nestedClass(rawType.simpleName(), typeArgumentNames) else
ParameterizedTypeName(null, rawType, typeArgumentNames)
}
override fun visitError(t: ErrorType, p: Void?): TypeName {
return visitDeclared(t, p)
}
override fun visitArray(t: ArrayType, p: Void?): ArrayTypeName {
return ArrayTypeName.get(t, typeVariables)
}
override fun visitTypeVariable(t: javax.lang.model.type.TypeVariable, p: Void?): TypeName {
return TypeVariableName.get(t, typeVariables.toMutableMap())
}
override fun visitWildcard(t: javax.lang.model.type.WildcardType, p: Void?): TypeName {
return WildcardTypeName.get(t, typeVariables)
}
override fun visitNoType(t: NoType, p: Void?): TypeName {
if (t.kind == TypeKind.VOID) return UNIT
return super.visitUnknown(t, p)
}
override fun defaultAction(e: TypeMirror?, p: Void?): TypeName {
throw IllegalArgumentException("Unexpected type mirror: " + e!!)
}
}, null)
}
/** Returns a type name equivalent to `type`. */
@JvmStatic fun get(type: KClass<*>) = get(type.java)
/** Returns a type name equivalent to `type`. */
@JvmOverloads @JvmStatic fun get(
type: Type,
map: MutableMap<Type, TypeVariableName> = mutableMapOf())
: TypeName {
when (type) {
is Class<*> -> {
when {
type === Void.TYPE -> return UNIT
type === Boolean::class.javaPrimitiveType -> return BOOLEAN
type === Byte::class.javaPrimitiveType -> return BYTE
type === Short::class.javaPrimitiveType -> return SHORT
type === Int::class.javaPrimitiveType -> return INT
type === Long::class.javaPrimitiveType -> return LONG
type === Char::class.javaPrimitiveType -> return CHAR
type === Float::class.javaPrimitiveType -> return FLOAT
type === Double::class.javaPrimitiveType -> return DOUBLE
type.isArray -> return ArrayTypeName.of(get(type.componentType, map))
else -> return ClassName.get(type)
}
}
is ParameterizedType -> return ParameterizedTypeName.get(type, map)
is WildcardType -> return WildcardTypeName.get(type, map)
is TypeVariable<*> -> return TypeVariableName.get(type, map)
is GenericArrayType -> return ArrayTypeName.get(type, map)
else -> throw IllegalArgumentException("unexpected type: " + type)
}
}
/** Returns the array component of `type`, or null if `type` is not an array. */
@JvmStatic fun arrayComponent(type: TypeName): TypeName? {
return if (type is ArrayTypeName)
type.componentType else
null
}
}
}
@JvmField val ANY = ClassName.get("kotlin", "Any")
@JvmField val UNIT = ClassName.get(Unit::class)
@JvmField val BOOLEAN = ClassName.get("kotlin", "Boolean")
@JvmField val BYTE = ClassName.get("kotlin", "Byte")
@JvmField val SHORT = ClassName.get("kotlin", "Short")
@JvmField val INT = ClassName.get("kotlin", "Int")
@JvmField val LONG = ClassName.get("kotlin", "Long")
@JvmField val CHAR = ClassName.get("kotlin", "Char")
@JvmField val FLOAT = ClassName.get("kotlin", "Float")
@JvmField val DOUBLE = ClassName.get("kotlin", "Double")