xref: /aosp_15_r20/external/kotlinpoet/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ParameterSpec.kt (revision 3c321d951dd070fb96f8ba59e952ffc3131379a0)
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