blob: 4b36788b613d46f2da10958dc3c02e67cc81745a [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 com.squareup.kotlinpoet.FunSpec.Companion.GETTER
import com.squareup.kotlinpoet.FunSpec.Companion.SETTER
import java.lang.reflect.Type
import kotlin.reflect.KClass
/** A generated property declaration. */
class PropertySpec private constructor(builder: Builder) {
val mutable = builder.mutable
val name = builder.name
val type = builder.type
val kdoc = builder.kdoc.build()
val annotations = builder.annotations.toImmutableList()
val modifiers = builder.modifiers.toImmutableSet()
val initializer = builder.initializer
val delegated = builder.delegated
val getter = builder.getter
val setter = builder.setter
val receiverType = builder.receiverType
internal fun emit(
codeWriter: CodeWriter,
implicitModifiers: Set<KModifier>,
withInitializer: Boolean = true,
inline: Boolean = false) {
val isInlineProperty = getter?.modifiers?.contains(KModifier.INLINE) ?: false &&
setter?.modifiers?.contains(KModifier.INLINE) ?: false
val propertyModifiers = if (isInlineProperty) modifiers + KModifier.INLINE else modifiers
codeWriter.emitKdoc(kdoc)
codeWriter.emitAnnotations(annotations, false)
codeWriter.emitModifiers(propertyModifiers, implicitModifiers)
codeWriter.emit(if (mutable) "var " else "val ")
if (receiverType != null) {
if (receiverType is LambdaTypeName) {
codeWriter.emitCode("(%T).", receiverType)
} else {
codeWriter.emitCode("%T.", receiverType)
}
}
codeWriter.emitCode("%L: %T", name, type)
if (withInitializer && initializer != null) {
if (delegated) {
codeWriter.emit(" by ")
} else {
codeWriter.emitCode(" =%W")
}
codeWriter.emitCode("%[%L%]", initializer)
}
if (!inline) codeWriter.emit("\n")
val implicitAccessorModifiers = if (isInlineProperty) {
implicitModifiers + KModifier.INLINE
} else {
implicitModifiers
}
if (getter != null) {
codeWriter.emitCode("%>")
getter.emit(codeWriter, null, implicitAccessorModifiers)
codeWriter.emitCode("%<")
}
if (setter != null) {
codeWriter.emitCode("%>")
setter.emit(codeWriter, null, implicitAccessorModifiers)
codeWriter.emitCode("%<")
}
}
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() = toString().hashCode()
override fun toString() = buildString { emit(CodeWriter(this), emptySet()) }
fun toBuilder(): Builder {
val builder = Builder(name, type)
builder.kdoc.add(kdoc)
builder.annotations += annotations
builder.modifiers += modifiers
builder.initializer = initializer
builder.delegated = delegated
return builder
}
class Builder internal constructor(internal val name: String, internal val type: TypeName) {
internal var mutable = false
internal val kdoc = CodeBlock.builder()
internal val annotations = mutableListOf<AnnotationSpec>()
internal val modifiers = mutableListOf<KModifier>()
internal var initializer: CodeBlock? = null
internal var delegated = false
internal var getter: FunSpec? = null
internal var setter: FunSpec? = null
internal var receiverType: TypeName? = null
fun mutable(mutable: Boolean) = apply {
this.mutable = mutable
}
fun addKdoc(format: String, vararg args: Any) = apply {
kdoc.add(format, *args)
}
fun addKdoc(block: CodeBlock) = apply {
kdoc.add(block)
}
fun addAnnotations(annotationSpecs: Iterable<AnnotationSpec>) = apply {
annotations += annotationSpecs
}
fun addAnnotation(annotationSpec: AnnotationSpec) = apply {
annotations += annotationSpec
}
fun addAnnotation(annotation: ClassName) = apply {
annotations += AnnotationSpec.builder(annotation).build()
}
fun addAnnotation(annotation: Class<*>) = addAnnotation(annotation.asClassName())
fun addAnnotation(annotation: KClass<*>) = addAnnotation(annotation.asClassName())
fun addModifiers(vararg modifiers: KModifier) = apply {
for (modifier in modifiers) {
modifier.checkTarget(KModifier.Target.PROPERTY)
}
this.modifiers += modifiers
}
fun initializer(format: String, vararg args: Any?) = initializer(CodeBlock.of(format, *args))
fun initializer(codeBlock: CodeBlock) = apply {
check(this.initializer == null) { "initializer was already set" }
this.initializer = codeBlock
}
fun delegate(format: String, vararg args: Any?): Builder = delegate(CodeBlock.of(format, *args))
fun delegate(codeBlock: CodeBlock) = apply {
check(this.initializer == null) { "initializer was already set" }
this.initializer = codeBlock
this.delegated = true
}
fun getter(getter: FunSpec) = apply {
require(getter.name == GETTER) { "${getter.name} is not a getter" }
check(this.getter == null) { "getter was already set" }
this.getter = getter
}
fun setter(setter: FunSpec) = apply {
require(setter.name == SETTER) { "${setter.name} is not a setter" }
check(this.setter == null) { "setter was already set" }
this.setter = setter
}
fun receiver(receiverType: TypeName) = apply {
this.receiverType = receiverType
}
fun receiver(receiverType: Type) = receiver(receiverType.asTypeName())
fun receiver(receiverType: KClass<*>) = receiver(receiverType.asTypeName())
fun build() = PropertySpec(this)
}
companion object {
@JvmStatic fun builder(name: String, type: TypeName, vararg modifiers: KModifier): Builder {
require(name.isName) { "not a valid name: $name" }
return Builder(name, type)
.addModifiers(*modifiers)
}
@JvmStatic fun builder(name: String, type: Type, vararg modifiers: KModifier)
= builder(name, type.asTypeName(), *modifiers)
@JvmStatic fun builder(name: String, type: KClass<*>, vararg modifiers: KModifier)
= builder(name, type.asTypeName(), *modifiers)
@JvmStatic fun varBuilder(name: String, type: TypeName, vararg modifiers: KModifier): Builder {
require(name.isName) { "not a valid name: $name" }
return Builder(name, type)
.mutable(true)
.addModifiers(*modifiers)
}
@JvmStatic fun varBuilder(name: String, type: Type, vararg modifiers: KModifier)
= varBuilder(name, type.asTypeName(), *modifiers)
@JvmStatic fun varBuilder(name: String, type: KClass<*>, vararg modifiers: KModifier)
= varBuilder(name, type.asTypeName(), *modifiers)
}
}