• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
33   public val name: String = builder.name
34   public val kdoc: CodeBlock = builder.kdoc.build()
35   public 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     emitKdoc: Boolean = false,
58     inlineAnnotations: Boolean = true,
59   ) {
60     if (emitKdoc) codeWriter.emitKdoc(kdoc.ensureEndsWithNewLine())
61     codeWriter.emitAnnotations(annotations, inlineAnnotations)
62     codeWriter.emitModifiers(modifiers)
63     if (name.isNotEmpty()) codeWriter.emitCode("%N", this)
64     if (name.isNotEmpty() && includeType) codeWriter.emitCode(":·")
65     if (includeType) codeWriter.emitCode("%T", type)
66     emitDefaultValue(codeWriter)
67   }
68 
69   internal fun emitDefaultValue(codeWriter: CodeWriter) {
70     if (defaultValue != null) {
71       codeWriter.emitCode(if (defaultValue.hasStatements()) " = %L" else " = «%L»", defaultValue)
72     }
73   }
74 
75   override fun equals(other: Any?): Boolean {
76     if (this === other) return true
77     if (other == null) return false
78     if (javaClass != other.javaClass) return false
79     return toString() == other.toString()
80   }
81 
82   override fun hashCode(): Int = toString().hashCode()
83 
84   override fun toString(): String = buildCodeString { emit(this) }
85 
86   public fun toBuilder(name: String = this.name, type: TypeName = this.type): Builder {
87     val builder = Builder(name, type)
88     builder.kdoc.add(kdoc)
89     builder.annotations += annotations
90     builder.modifiers += modifiers
91     builder.defaultValue = defaultValue
92     builder.tags += tagMap.tags
93     return builder
94   }
95 
96   public class Builder internal constructor(
97     internal val name: String,
98     internal val type: TypeName,
99   ) : Taggable.Builder<Builder> {
100     internal var defaultValue: CodeBlock? = null
101 
102     public val kdoc: CodeBlock.Builder = CodeBlock.builder()
103     public val annotations: MutableList<AnnotationSpec> = mutableListOf()
104     public val modifiers: MutableList<KModifier> = mutableListOf()
105     override val tags: MutableMap<KClass<*>, Any> = mutableMapOf()
106 
107     public fun addKdoc(format: String, vararg args: Any): Builder = apply {
108       kdoc.add(format, *args)
109     }
110 
111     public fun addKdoc(block: CodeBlock): Builder = apply {
112       kdoc.add(block)
113     }
114 
115     public fun addAnnotations(annotationSpecs: Iterable<AnnotationSpec>): Builder = apply {
116       annotations += annotationSpecs
117     }
118 
119     public fun addAnnotation(annotationSpec: AnnotationSpec): Builder = apply {
120       annotations += annotationSpec
121     }
122 
123     public fun addAnnotation(annotation: ClassName): Builder = apply {
124       annotations += AnnotationSpec.builder(annotation).build()
125     }
126 
127     public fun addAnnotation(annotation: Class<*>): Builder =
128       addAnnotation(annotation.asClassName())
129 
130     public fun addAnnotation(annotation: KClass<*>): Builder =
131       addAnnotation(annotation.asClassName())
132 
133     public fun addModifiers(vararg modifiers: KModifier): Builder = apply {
134       this.modifiers += modifiers
135     }
136 
137     public fun addModifiers(modifiers: Iterable<KModifier>): Builder = apply {
138       this.modifiers += modifiers
139     }
140 
141     @Deprecated(
142       "There are no jvm modifiers applicable to parameters in Kotlin",
143       ReplaceWith(""),
144       level = ERROR,
145     )
146     public fun jvmModifiers(modifiers: Iterable<Modifier>): Builder = apply {
147       throw IllegalArgumentException("JVM modifiers are not permitted on parameters in Kotlin")
148     }
149 
150     public fun defaultValue(format: String, vararg args: Any?): Builder =
151       defaultValue(CodeBlock.of(format, *args))
152 
153     public fun defaultValue(codeBlock: CodeBlock?): Builder = apply {
154       this.defaultValue = codeBlock
155     }
156 
157     public fun build(): ParameterSpec = ParameterSpec(this)
158   }
159 
160   public companion object {
161     @DelicateKotlinPoetApi(
162       message = "Element APIs don't give complete information on Kotlin types. Consider using" +
163         " the kotlinpoet-metadata APIs instead.",
164     )
165     @JvmStatic
166     public fun get(element: VariableElement): ParameterSpec {
167       val name = element.simpleName.toString()
168       val type = element.asType().asTypeName()
169       return builder(name, type)
170         .build()
171     }
172 
173     @DelicateKotlinPoetApi(
174       message = "Element APIs don't give complete information on Kotlin types. Consider using" +
175         " the kotlinpoet-metadata APIs instead.",
176     )
177     @JvmStatic
178     public fun parametersOf(method: ExecutableElement): List<ParameterSpec> =
179       method.parameters.map(::get)
180 
181     @JvmStatic public fun builder(
182       name: String,
183       type: TypeName,
184       vararg modifiers: KModifier,
185     ): Builder {
186       return Builder(name, type).addModifiers(*modifiers)
187     }
188 
189     @JvmStatic public fun builder(name: String, type: Type, vararg modifiers: KModifier): Builder =
190       builder(name, type.asTypeName(), *modifiers)
191 
192     @JvmStatic public fun builder(
193       name: String,
194       type: KClass<*>,
195       vararg modifiers: KModifier,
196     ): Builder = builder(name, type.asTypeName(), *modifiers)
197 
198     @JvmStatic public fun builder(
199       name: String,
200       type: TypeName,
201       modifiers: Iterable<KModifier>,
202     ): Builder {
203       return Builder(name, type).addModifiers(modifiers)
204     }
205 
206     @JvmStatic public fun builder(
207       name: String,
208       type: Type,
209       modifiers: Iterable<KModifier>,
210     ): Builder = builder(name, type.asTypeName(), modifiers)
211 
212     @JvmStatic public fun builder(
213       name: String,
214       type: KClass<*>,
215       modifiers: Iterable<KModifier>,
216     ): Builder = builder(name, type.asTypeName(), modifiers)
217 
218     @JvmStatic public fun unnamed(type: KClass<*>): ParameterSpec = unnamed(type.asTypeName())
219 
220     @JvmStatic public fun unnamed(type: Type): ParameterSpec = unnamed(type.asTypeName())
221 
222     @JvmStatic public fun unnamed(type: TypeName): ParameterSpec = Builder("", type).build()
223   }
224 }
225 
226 // From https://kotlinlang.org/spec/syntax-and-grammar.html#grammar-rule-parameterModifier
227 private val ALLOWED_PARAMETER_MODIFIERS = setOf(VARARG, NOINLINE, CROSSINLINE)
228 
emitnull229 internal fun List<ParameterSpec>.emit(
230   codeWriter: CodeWriter,
231   forceNewLines: Boolean = false,
232   emitBlock: (ParameterSpec) -> Unit = { it.emit(codeWriter) },
<lambda>null233 ) = with(codeWriter) {
234   emit("(")
235 
236   if (isNotEmpty()) {
237     val emitNewLines = size > 2 || forceNewLines
238     if (emitNewLines) {
239       emit("\n")
240       indent(1)
241     }
242     forEachIndexed { index, parameter ->
243       if (index > 0) {
244         emit(if (emitNewLines) "\n" else ", ")
245       }
246       emitBlock(parameter)
247       if (emitNewLines) {
248         emit(",")
249       }
250     }
251     if (emitNewLines) {
252       unindent(1)
253       emit("\n")
254     }
255   }
256   emit(")")
257 }
258