1 /*
<lambda>null2  * Copyright (C) 2017 The Android Open Source Project
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  *      http://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 
17 package com.android.tools.metalava
18 
19 import com.android.tools.metalava.model.ClassItem
20 import com.android.tools.metalava.model.ConstructorItem
21 import com.android.tools.metalava.model.ExceptionTypeItem
22 import com.android.tools.metalava.model.FieldItem
23 import com.android.tools.metalava.model.Item
24 import com.android.tools.metalava.model.MethodItem
25 import com.android.tools.metalava.model.ModifierListWriter
26 import com.android.tools.metalava.model.PackageItem
27 import com.android.tools.metalava.model.PropertyItem
28 import com.android.tools.metalava.model.TypeItem
29 import com.android.tools.metalava.model.TypeParameterList
30 import com.android.tools.metalava.model.text.FileFormat
31 import com.android.tools.metalava.model.visitors.ApiVisitor
32 import java.io.PrintWriter
33 import java.util.BitSet
34 import java.util.function.Predicate
35 
36 class SignatureWriter(
37     private val writer: PrintWriter,
38     filterEmit: Predicate<Item>,
39     filterReference: Predicate<Item>,
40     private val preFiltered: Boolean,
41     private var emitHeader: EmitFileHeader = EmitFileHeader.ALWAYS,
42     private val fileFormat: FileFormat,
43     showUnannotated: Boolean,
44     apiVisitorConfig: Config,
45 ) :
46     ApiVisitor(
47         visitConstructorsAsMethods = false,
48         nestInnerClasses = false,
49         inlineInheritedFields = true,
50         methodComparator = fileFormat.overloadedMethodOrder.comparator,
51         filterEmit = filterEmit,
52         filterReference = filterReference,
53         showUnannotated = showUnannotated,
54         config = apiVisitorConfig,
55     ) {
56     init {
57         // If a header must always be written out (even if the file is empty) then write it here.
58         if (emitHeader == EmitFileHeader.ALWAYS) {
59             writer.print(fileFormat.header())
60         }
61     }
62 
63     private val modifierListWriter =
64         ModifierListWriter.forSignature(
65             writer = writer,
66             skipNullnessAnnotations = fileFormat.kotlinStyleNulls,
67         )
68 
69     internal fun write(text: String) {
70         // If a header must only be written out when the file is not empty then write it here as
71         // this is not called
72         if (emitHeader == EmitFileHeader.IF_NONEMPTY_FILE) {
73             writer.print(fileFormat.header())
74             // Remember that the header was written out, so it will not be written again.
75             emitHeader = EmitFileHeader.NEVER
76         }
77         writer.print(text)
78     }
79 
80     override fun visitPackage(pkg: PackageItem) {
81         write("package ")
82         writeModifiers(pkg)
83         write("${pkg.qualifiedName()} {\n\n")
84     }
85 
86     override fun afterVisitPackage(pkg: PackageItem) {
87         write("}\n\n")
88     }
89 
90     override fun visitConstructor(constructor: ConstructorItem) {
91         fun writeConstructor(skipMask: BitSet? = null) {
92             write("    ctor ")
93             writeModifiers(constructor)
94             writeTypeParameterList(constructor.typeParameterList, addSpace = true)
95             write(constructor.containingClass().fullName())
96             writeParameterList(constructor, skipMask)
97             writeThrowsList(constructor)
98             write(";\n")
99         }
100 
101         // Workaround for https://youtrack.jetbrains.com/issue/KT-57537
102         if (constructor.shouldExpandOverloads()) {
103             val parameters = constructor.parameters()
104             val defaultMask = BitSet(parameters.size)
105 
106             // fill the bitmask for all parameters
107             parameters.forEachIndexed { i, item -> defaultMask.set(i, item.hasDefaultValue()) }
108 
109             // expand overloads ordered by number of parameters, skipping last parameters first
110             for (i in parameters.indices) {
111                 if (!defaultMask.get(i)) continue
112                 writeConstructor(defaultMask)
113                 defaultMask.clear(i)
114             }
115         }
116 
117         writeConstructor()
118     }
119 
120     override fun visitField(field: FieldItem) {
121         val name = if (field.isEnumConstant()) "enum_constant" else "field"
122         write("    ")
123         write(name)
124         write(" ")
125         writeModifiers(field)
126 
127         if (fileFormat.kotlinNameTypeOrder) {
128             // Kotlin style: write the name of the field, then the type.
129             write(field.name())
130             write(": ")
131             writeType(field.type())
132         } else {
133             // Java style: write the type, then the name of the field.
134             writeType(field.type())
135             write(" ")
136             write(field.name())
137         }
138 
139         field.writeValueWithSemicolon(
140             writer,
141             allowDefaultValue = false,
142             requireInitialValue = false
143         )
144         write("\n")
145     }
146 
147     override fun visitProperty(property: PropertyItem) {
148         write("    property ")
149         writeModifiers(property)
150         if (fileFormat.kotlinNameTypeOrder) {
151             // Kotlin style: write the name of the property, then the type.
152             write(property.name())
153             write(": ")
154             writeType(property.type())
155         } else {
156             // Java style: write the type, then the name of the property.
157             writeType(property.type())
158             write(" ")
159             write(property.name())
160         }
161         write(";\n")
162     }
163 
164     override fun visitMethod(method: MethodItem) {
165         write("    method ")
166         writeModifiers(method)
167         writeTypeParameterList(method.typeParameterList, addSpace = true)
168 
169         if (fileFormat.kotlinNameTypeOrder) {
170             // Kotlin style: write the name of the method and the parameters, then the type.
171             write(method.name())
172             writeParameterList(method)
173             write(": ")
174             writeType(method.returnType())
175         } else {
176             // Java style: write the type, then the name of the method and the parameters.
177             writeType(method.returnType())
178             write(" ")
179             write(method.name())
180             writeParameterList(method)
181         }
182 
183         writeThrowsList(method)
184 
185         if (method.containingClass().isAnnotationType()) {
186             val default = method.defaultValue()
187             if (default.isNotEmpty()) {
188                 write(" default ")
189                 write(default)
190             }
191         }
192 
193         write(";\n")
194     }
195 
196     override fun visitClass(cls: ClassItem) {
197         write("  ")
198 
199         writeModifiers(cls)
200 
201         if (cls.isAnnotationType()) {
202             write("@interface")
203         } else if (cls.isInterface()) {
204             write("interface")
205         } else if (cls.isEnum()) {
206             write("enum")
207         } else {
208             write("class")
209         }
210         write(" ")
211         write(cls.fullName())
212         writeTypeParameterList(cls.typeParameterList, addSpace = false)
213         writeSuperClassStatement(cls)
214         writeInterfaceList(cls)
215 
216         write(" {\n")
217     }
218 
219     override fun afterVisitClass(cls: ClassItem) {
220         write("  }\n\n")
221     }
222 
223     private fun writeModifiers(item: Item) {
224         modifierListWriter.write(item.actualItem)
225     }
226 
227     /** Get the filtered super class type, ignoring java.lang.Object. */
228     private fun getFilteredSuperClassTypeFor(cls: ClassItem): TypeItem? {
229         val superClassItem =
230             if (preFiltered) cls.superClassType() else cls.filteredSuperClassType(filterReference)
231         return if (superClassItem == null || superClassItem.isJavaLangObject()) null
232         else superClassItem
233     }
234 
235     private fun writeSuperClassStatement(cls: ClassItem) {
236         if (cls.isEnum() || cls.isAnnotationType() || cls.isInterface()) {
237             return
238         }
239 
240         getFilteredSuperClassTypeFor(cls)?.let { superClassType ->
241             write(" extends")
242             writeExtendsOrImplementsType(superClassType)
243         }
244     }
245 
246     private fun writeExtendsOrImplementsType(typeItem: TypeItem) {
247         val superClassString =
248             typeItem.toTypeString(
249                 annotations = fileFormat.includeTypeUseAnnotations,
250                 kotlinStyleNulls = fileFormat.kotlinStyleNulls,
251                 filter = filterReference
252             )
253         write(" ")
254         write(superClassString)
255     }
256 
257     private fun writeInterfaceList(cls: ClassItem) {
258         if (cls.isAnnotationType()) {
259             return
260         }
261         val isInterface = cls.isInterface()
262 
263         val unfilteredInterfaceTypes = cls.interfaceTypes()
264         val interfaces =
265             if (preFiltered) unfilteredInterfaceTypes
266             else cls.filteredInterfaceTypes(filterReference)
267         if (interfaces.isEmpty()) {
268             return
269         }
270 
271         // Sort before prepending the super class (if this is an interface) as the super class
272         // always comes first because it was previously written out by writeSuperClassStatement.
273         @Suppress("DEPRECATION")
274         val comparator =
275             if (fileFormat.sortWholeExtendsList) TypeItem.totalComparator
276             else TypeItem.partialComparator
277         val sortedInterfaces = interfaces.sortedWith(comparator)
278 
279         // Combine the super class and interfaces into a full list of them.
280         val fullInterfaces =
281             if (isInterface && !fileFormat.sortWholeExtendsList) {
282                 // Previously, when the first interface in the extends list was stored in
283                 // superClass, if that interface was visible in the signature then it would always
284                 // be first even though the other interfaces are sorted in alphabetical order. This
285                 // implements similar logic.
286                 val firstUnfilteredInterfaceType = unfilteredInterfaceTypes.first()
287                 val firstFilteredInterfaceType = interfaces.first()
288                 if (firstFilteredInterfaceType == firstUnfilteredInterfaceType) {
289                     buildList {
290                         // The first interface in the interfaces list is also the first interface in
291                         // the filtered interfaces list so add it first.
292                         add(firstFilteredInterfaceType)
293 
294                         // Add the remaining interfaces in sorted order.
295                         if (sortedInterfaces.size > 1) {
296                             for (interfaceType in sortedInterfaces) {
297                                 if (interfaceType != firstFilteredInterfaceType) {
298                                     add(interfaceType)
299                                 }
300                             }
301                         }
302                     }
303                 } else {
304                     sortedInterfaces
305                 }
306             } else sortedInterfaces
307 
308         val label = if (isInterface) " extends" else " implements"
309         write(label)
310 
311         fullInterfaces.forEach { typeItem -> writeExtendsOrImplementsType(typeItem) }
312     }
313 
314     private fun writeTypeParameterList(typeList: TypeParameterList, addSpace: Boolean) {
315         val typeListString = typeList.toString()
316         if (typeListString.isNotEmpty()) {
317             write(typeListString)
318             if (addSpace) {
319                 write(" ")
320             }
321         }
322     }
323 
324     private fun writeParameterList(method: MethodItem, skipMask: BitSet? = null) {
325         write("(")
326         var writtenParams = 0
327         method.parameters().asSequence().forEachIndexed { i, parameter ->
328             // skip over defaults when generating @JvmOverloads permutations
329             if (skipMask != null && skipMask.get(i)) return@forEachIndexed
330 
331             if (writtenParams > 0) {
332                 write(", ")
333             }
334             if (parameter.hasDefaultValue() && fileFormat.conciseDefaultValues) {
335                 // Concise representation of a parameter with a default
336                 write("optional ")
337             }
338             writeModifiers(parameter)
339 
340             if (fileFormat.kotlinNameTypeOrder) {
341                 // Kotlin style: the parameter must have a name (use `_` if it doesn't have a public
342                 // name). Write the name and then the type.
343                 val name = parameter.publicName() ?: "_"
344                 write(name)
345                 write(": ")
346                 writeType(parameter.type())
347             } else {
348                 // Java style: write the type, then the name if it has a public name.
349                 writeType(parameter.type())
350                 val name = parameter.publicName()
351                 if (name != null) {
352                     write(" ")
353                     write(name)
354                 }
355             }
356 
357             if (parameter.isDefaultValueKnown() && !fileFormat.conciseDefaultValues) {
358                 write(" = ")
359                 val defaultValue = parameter.defaultValue()
360                 if (defaultValue != null) {
361                     write(defaultValue)
362                 } else {
363                     // null is a valid default value!
364                     write("null")
365                 }
366             }
367             writtenParams++
368         }
369         write(")")
370     }
371 
372     private fun writeType(type: TypeItem?) {
373         type ?: return
374 
375         var typeString =
376             type.toTypeString(
377                 annotations = fileFormat.includeTypeUseAnnotations,
378                 kotlinStyleNulls = fileFormat.kotlinStyleNulls,
379                 filter = filterReference
380             )
381 
382         // Strip java.lang. prefix
383         typeString = TypeItem.shortenTypes(typeString)
384 
385         write(typeString)
386     }
387 
388     private fun writeThrowsList(method: MethodItem) {
389         val throws =
390             when {
391                 preFiltered -> method.throwsTypes().asSequence()
392                 else -> method.filteredThrowsTypes(filterReference).asSequence()
393             }
394         if (throws.any()) {
395             write(" throws ")
396             throws.asSequence().sortedWith(ExceptionTypeItem.fullNameComparator).forEachIndexed {
397                 i,
398                 type ->
399                 if (i > 0) {
400                     write(", ")
401                 }
402                 write(type.toTypeString())
403             }
404         }
405     }
406 }
407 
408 enum class EmitFileHeader {
409     ALWAYS,
410     NEVER,
411     IF_NONEMPTY_FILE
412 }
413