1 /*
<lambda>null2 * 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 package com.squareup.kotlinpoet
17
18 import com.squareup.kotlinpoet.KModifier.CROSSINLINE
19 import com.squareup.kotlinpoet.KModifier.NOINLINE
20 import com.squareup.kotlinpoet.KModifier.VARARG
21 import java.lang.reflect.Type
22 import javax.lang.model.element.ExecutableElement
23 import javax.lang.model.element.Modifier
24 import javax.lang.model.element.VariableElement
25 import kotlin.DeprecationLevel.ERROR
26 import kotlin.reflect.KClass
27
28 /** A generated parameter declaration. */
29 public class ParameterSpec private constructor(
30 builder: Builder,
31 private val tagMap: TagMap = builder.buildTagMap(),
32 ) : Taggable by tagMap, Annotatable, Documentable {
33 public val name: String = builder.name
34 override val kdoc: CodeBlock = builder.kdoc.build()
35 override val annotations: List<AnnotationSpec> = builder.annotations.toImmutableList()
36 public val modifiers: Set<KModifier> = builder.modifiers
37 .also {
38 LinkedHashSet(it).apply {
39 removeAll(ALLOWED_PARAMETER_MODIFIERS)
40 if (!isEmpty()) {
41 throw IllegalArgumentException("Modifiers $this are not allowed on Kotlin parameters. Allowed modifiers: $ALLOWED_PARAMETER_MODIFIERS")
42 }
43 }
44 }
45 .toImmutableSet()
46 public val type: TypeName = builder.type
47 public val defaultValue: CodeBlock? = builder.defaultValue
48
49 public constructor(name: String, type: TypeName, vararg modifiers: KModifier) :
50 this(builder(name, type, *modifiers))
51 public constructor(name: String, type: TypeName, modifiers: Iterable<KModifier>) :
52 this(builder(name, type, modifiers))
53
54 internal fun emit(
55 codeWriter: CodeWriter,
56 includeType: Boolean = true,
57 inlineAnnotations: Boolean = true,
58 ) {
59 codeWriter.emitAnnotations(annotations, inlineAnnotations)
60 codeWriter.emitModifiers(modifiers)
61 if (name.isNotEmpty()) codeWriter.emitCode("%N", this)
62 if (name.isNotEmpty() && includeType) codeWriter.emitCode(":·")
63 if (includeType) codeWriter.emitCode("%T", type)
64 emitDefaultValue(codeWriter)
65 }
66
67 internal fun emitDefaultValue(codeWriter: CodeWriter) {
68 if (defaultValue != null) {
69 codeWriter.emitCode(if (defaultValue.hasStatements()) " = %L" else " = «%L»", defaultValue)
70 }
71 }
72
73 override fun equals(other: Any?): Boolean {
74 if (this === other) return true
75 if (other == null) return false
76 if (javaClass != other.javaClass) return false
77 return toString() == other.toString()
78 }
79
80 override fun hashCode(): Int = toString().hashCode()
81
82 override fun toString(): String = buildCodeString { emit(this) }
83
84 public fun toBuilder(name: String = this.name, type: TypeName = this.type): Builder {
85 val builder = Builder(name, type)
86 builder.kdoc.add(kdoc)
87 builder.annotations += annotations
88 builder.modifiers += modifiers
89 builder.defaultValue = defaultValue
90 builder.tags += tagMap.tags
91 return builder
92 }
93
94 public class Builder internal constructor(
95 internal val name: String,
96 internal val type: TypeName,
97 ) : Taggable.Builder<Builder>, Annotatable.Builder<Builder>, Documentable.Builder<Builder> {
98 internal var defaultValue: CodeBlock? = null
99
100 public val modifiers: MutableList<KModifier> = mutableListOf()
101 override val kdoc: CodeBlock.Builder = CodeBlock.builder()
102 override val tags: MutableMap<KClass<*>, Any> = mutableMapOf()
103 override val annotations: MutableList<AnnotationSpec> = mutableListOf()
104
105 public fun addModifiers(vararg modifiers: KModifier): Builder = apply {
106 this.modifiers += modifiers
107 }
108
109 public fun addModifiers(modifiers: Iterable<KModifier>): Builder = apply {
110 this.modifiers += modifiers
111 }
112
113 @Deprecated(
114 "There are no jvm modifiers applicable to parameters in Kotlin",
115 ReplaceWith(""),
116 level = ERROR,
117 )
118 public fun jvmModifiers(@Suppress("UNUSED_PARAMETER", "unused") modifiers: Iterable<Modifier>): Builder = apply {
119 throw IllegalArgumentException("JVM modifiers are not permitted on parameters in Kotlin")
120 }
121
122 public fun defaultValue(format: String, vararg args: Any?): Builder =
123 defaultValue(CodeBlock.of(format, *args))
124
125 public fun defaultValue(codeBlock: CodeBlock?): Builder = apply {
126 this.defaultValue = codeBlock
127 }
128
129 //region Overrides for binary compatibility
130 @Suppress("RedundantOverride")
131 override fun addAnnotation(annotationSpec: AnnotationSpec): Builder = super.addAnnotation(annotationSpec)
132
133 @Suppress("RedundantOverride")
134 override fun addAnnotations(annotationSpecs: Iterable<AnnotationSpec>): Builder =
135 super.addAnnotations(annotationSpecs)
136
137 @Suppress("RedundantOverride")
138 override fun addAnnotation(annotation: ClassName): Builder = super.addAnnotation(annotation)
139
140 @DelicateKotlinPoetApi(
141 message = "Java reflection APIs don't give complete information on Kotlin types. Consider " +
142 "using the kotlinpoet-metadata APIs instead.",
143 )
144 override fun addAnnotation(annotation: Class<*>): Builder = super.addAnnotation(annotation)
145
146 @Suppress("RedundantOverride")
147 override fun addAnnotation(annotation: KClass<*>): Builder = super.addAnnotation(annotation)
148
149 @Suppress("RedundantOverride")
150 override fun addKdoc(format: String, vararg args: Any): Builder = super.addKdoc(format, *args)
151
152 @Suppress("RedundantOverride")
153 override fun addKdoc(block: CodeBlock): Builder = super.addKdoc(block)
154 //endregion
155
156 public fun build(): ParameterSpec = ParameterSpec(this)
157 }
158
159 public companion object {
160 @DelicateKotlinPoetApi(
161 message = "Element APIs don't give complete information on Kotlin types. Consider using" +
162 " the kotlinpoet-metadata APIs instead.",
163 )
164 @JvmStatic
165 public fun get(element: VariableElement): ParameterSpec {
166 val name = element.simpleName.toString()
167 val type = element.asType().asTypeName()
168 return builder(name, type)
169 .build()
170 }
171
172 @DelicateKotlinPoetApi(
173 message = "Element APIs don't give complete information on Kotlin types. Consider using" +
174 " the kotlinpoet-metadata APIs instead.",
175 )
176 @JvmStatic
177 public fun parametersOf(method: ExecutableElement): List<ParameterSpec> =
178 method.parameters.map(::get)
179
180 @JvmStatic public fun builder(
181 name: String,
182 type: TypeName,
183 vararg modifiers: KModifier,
184 ): Builder {
185 return Builder(name, type).addModifiers(*modifiers)
186 }
187
188 @JvmStatic public fun builder(name: String, type: Type, vararg modifiers: KModifier): Builder =
189 builder(name, type.asTypeName(), *modifiers)
190
191 @JvmStatic public fun builder(
192 name: String,
193 type: KClass<*>,
194 vararg modifiers: KModifier,
195 ): Builder = builder(name, type.asTypeName(), *modifiers)
196
197 @JvmStatic public fun builder(
198 name: String,
199 type: TypeName,
200 modifiers: Iterable<KModifier>,
201 ): Builder {
202 return Builder(name, type).addModifiers(modifiers)
203 }
204
205 @JvmStatic public fun builder(
206 name: String,
207 type: Type,
208 modifiers: Iterable<KModifier>,
209 ): Builder = builder(name, type.asTypeName(), modifiers)
210
211 @JvmStatic public fun builder(
212 name: String,
213 type: KClass<*>,
214 modifiers: Iterable<KModifier>,
215 ): Builder = builder(name, type.asTypeName(), modifiers)
216
217 @JvmStatic public fun unnamed(type: KClass<*>): ParameterSpec = unnamed(type.asTypeName())
218
219 @JvmStatic public fun unnamed(type: Type): ParameterSpec = unnamed(type.asTypeName())
220
221 @JvmStatic public fun unnamed(type: TypeName): ParameterSpec = Builder("", type).build()
222 }
223 }
224
225 // From https://kotlinlang.org/spec/syntax-and-grammar.html#grammar-rule-parameterModifier
226 private val ALLOWED_PARAMETER_MODIFIERS = setOf(VARARG, NOINLINE, CROSSINLINE)
227
emitnull228 internal fun List<ParameterSpec>.emit(
229 codeWriter: CodeWriter,
230 forceNewLines: Boolean = false,
231 emitBlock: (ParameterSpec) -> Unit = { it.emit(codeWriter) },
<lambda>null232 ) = with(codeWriter) {
233 emit("(")
234
235 if (isNotEmpty()) {
236 val emitNewLines = size > 2 || forceNewLines
237 if (emitNewLines) {
238 emit("\n")
239 indent(1)
240 }
241 forEachIndexed { index, parameter ->
242 if (index > 0) {
243 emit(if (emitNewLines) "\n" else ", ")
244 }
245 emitBlock(parameter)
246 if (emitNewLines) {
247 emit(",")
248 }
249 }
250 if (emitNewLines) {
251 unindent(1)
252 emit("\n")
253 }
254 }
255 emit(")")
256 }
257