• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.AnnotationTarget
20 import com.android.tools.metalava.model.ClassItem
21 import com.android.tools.metalava.model.ConstructorItem
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.ModifierList
26 import com.android.tools.metalava.model.PackageItem
27 import com.android.tools.metalava.model.ParameterItem
28 import com.android.tools.metalava.model.PropertyItem
29 import com.android.tools.metalava.model.TypeItem
30 import com.android.tools.metalava.model.TypeParameterList
31 import com.android.tools.metalava.model.visitors.ApiVisitor
32 import java.io.PrintWriter
33 import java.util.function.Predicate
34 
35 class SignatureWriter(
36     private val writer: PrintWriter,
37     filterEmit: Predicate<Item>,
38     filterReference: Predicate<Item>,
39     private val preFiltered: Boolean
40 ) : ApiVisitor(
41     visitConstructorsAsMethods = false,
42     nestInnerClasses = false,
43     inlineInheritedFields = true,
44     methodComparator = MethodItem.comparator,
45     fieldComparator = FieldItem.comparator,
46     filterEmit = filterEmit,
47     filterReference = filterReference,
48     showUnannotated = options.showUnannotated
49 ) {
50     override fun skip(item: Item): Boolean {
51         return super.skip(item) || item is ClassItem && item.notStrippable
52     }
53 
54     init {
55         if (options.includeSignatureFormatVersion) {
56             writer.print(options.outputFormat.header())
57         }
58     }
59 
60     override fun visitPackage(pkg: PackageItem) {
61         writer.print("package ")
62         writeModifiers(pkg)
63         writer.print("${pkg.qualifiedName()} {\n\n")
64     }
65 
66     override fun afterVisitPackage(pkg: PackageItem) {
67         writer.print("}\n\n")
68     }
69 
70     override fun visitConstructor(constructor: ConstructorItem) {
71         writer.print("    ctor ")
72         writeModifiers(constructor)
73         // Note - we don't write out the type parameter list (constructor.typeParameterList()) in signature files!
74         // writeTypeParameterList(constructor.typeParameterList(), addSpace = true)
75         writer.print(constructor.containingClass().fullName())
76         writeParameterList(constructor)
77         writeThrowsList(constructor)
78         writer.print(";\n")
79     }
80 
81     override fun visitField(field: FieldItem) {
82         if (compatibility.skipInheritedConstants && field.inheritedField) {
83             return
84         }
85 
86         val name = if (field.isEnumConstant()) "enum_constant" else "field"
87         writer.print("    ")
88         writer.print(name)
89         writer.print(" ")
90         writeModifiers(field)
91         writeType(field, field.type())
92         writer.print(' ')
93         writer.print(field.name())
94         field.writeValueWithSemicolon(writer, allowDefaultValue = false, requireInitialValue = false)
95         writer.print("\n")
96     }
97 
98     override fun visitProperty(property: PropertyItem) {
99         writer.print("    property ")
100         writeModifiers(property)
101         writeType(property, property.type())
102         writer.print(' ')
103         writer.print(property.name())
104         writer.print(";\n")
105     }
106 
107     override fun visitMethod(method: MethodItem) {
108         if (compatibility.skipAnnotationInstanceMethods && method.containingClass().isAnnotationType() &&
109             !method.modifiers.isStatic()
110         ) {
111             return
112         }
113 
114         if (compatibility.skipInheritedMethods && method.inheritedMethod) {
115             return
116         }
117 
118         writer.print("    method ")
119         writeModifiers(method)
120         writeTypeParameterList(method.typeParameterList(), addSpace = true)
121 
122         writeType(method, method.returnType())
123         writer.print(' ')
124         writer.print(method.name())
125         writeParameterList(method)
126         writeThrowsList(method)
127 
128         if (compatibility.includeAnnotationDefaults) {
129             if (method.containingClass().isAnnotationType()) {
130                 val default = method.defaultValue()
131                 if (default.isNotEmpty()) {
132                     writer.print(" default ")
133                     writer.print(default)
134                 }
135             }
136         }
137 
138         writer.print(";\n")
139     }
140 
141     override fun visitClass(cls: ClassItem) {
142         writer.print("  ")
143 
144         writeModifiers(cls)
145 
146         if (cls.isAnnotationType()) {
147             if (compatibility.classForAnnotations) {
148                 // doclava incorrectly treats annotations (such as TargetApi) as an abstract class instead
149                 // of an @interface!
150                 //
151                 // Example:
152                 //   public abstract class SuppressLint implements java.lang.annotation.Annotation { }
153                 writer.print("class")
154             } else {
155                 writer.print("@interface")
156             }
157         } else if (cls.isInterface()) {
158             writer.print("interface")
159         } else if (!compatibility.classForEnums && cls.isEnum()) { // compat mode calls enums "class" instead
160             writer.print("enum")
161         } else {
162             writer.print("class")
163         }
164         writer.print(" ")
165         writer.print(cls.fullName())
166         writeTypeParameterList(cls.typeParameterList(), addSpace = false)
167         writeSuperClassStatement(cls)
168         writeInterfaceList(cls)
169 
170         writer.print(" {\n")
171     }
172 
173     override fun afterVisitClass(cls: ClassItem) {
174         writer.print("  }\n\n")
175     }
176 
177     private fun writeModifiers(item: Item) {
178         ModifierList.write(
179             writer = writer,
180             modifiers = item.modifiers,
181             item = item,
182             target = AnnotationTarget.SIGNATURE_FILE,
183             includeDeprecated = true,
184             includeAnnotations = compatibility.annotationsInSignatures,
185             skipNullnessAnnotations = options.outputKotlinStyleNulls,
186             omitCommonPackages = compatibility.omitCommonPackages
187         )
188     }
189 
190     private fun writeSuperClassStatement(cls: ClassItem) {
191         if (!compatibility.classForEnums && cls.isEnum() || cls.isAnnotationType()) {
192             return
193         }
194 
195         if (cls.isInterface() && compatibility.extendsForInterfaceSuperClass) {
196             // Written in the interface section instead
197             return
198         }
199 
200         val superClass = if (preFiltered)
201             cls.superClassType()
202         else cls.filteredSuperClassType(filterReference)
203         if (superClass != null && !superClass.isJavaLangObject()) {
204             val superClassString =
205                 superClass.toTypeString(
206                     erased = compatibility.omitTypeParametersInInterfaces,
207                     kotlinStyleNulls = false,
208                     context = superClass.asClass(),
209                     filter = filterReference
210                 )
211             writer.print(" extends ")
212             writer.print(superClassString)
213         }
214     }
215 
216     private fun writeInterfaceList(cls: ClassItem) {
217         if (cls.isAnnotationType()) {
218             if (compatibility.classForAnnotations) {
219                 writer.print(" implements java.lang.annotation.Annotation")
220             }
221             return
222         }
223         val isInterface = cls.isInterface()
224 
225         val interfaces = if (preFiltered)
226             cls.interfaceTypes().asSequence()
227         else cls.filteredInterfaceTypes(filterReference).asSequence()
228         val all: Sequence<TypeItem> = if (isInterface && compatibility.extendsForInterfaceSuperClass) {
229             val superClassType = cls.superClassType()
230             if (superClassType != null && !superClassType.isJavaLangObject()) {
231                 interfaces.plus(sequenceOf(superClassType))
232             } else {
233                 interfaces
234             }
235         } else {
236             interfaces
237         }
238 
239         if (all.any()) {
240             val label =
241                 if (isInterface && !compatibility.extendsForInterfaceSuperClass) {
242                     val superInterface = cls.filteredSuperclass(filterReference)
243                     if (superInterface != null && !superInterface.isJavaLangObject()) {
244                         // For interfaces we've already listed "extends <super interface>"; we don't
245                         // want to repeat "extends " here
246                         ""
247                     } else {
248                         " extends"
249                     }
250                 } else {
251                     " implements"
252                 }
253             writer.print(label)
254             all.sortedWith(TypeItem.comparator).forEach { item ->
255                 writer.print(" ")
256                 writer.print(
257                     item.toTypeString(
258                         erased = compatibility.omitTypeParametersInInterfaces,
259                         kotlinStyleNulls = false,
260                         context = item.asClass(),
261                         filter = filterReference
262                     )
263                 )
264             }
265         }
266     }
267 
268     private fun writeTypeParameterList(typeList: TypeParameterList, addSpace: Boolean) {
269         val typeListString = typeList.toString()
270         if (typeListString.isNotEmpty()) {
271             writer.print(typeListString)
272             if (addSpace) {
273                 writer.print(' ')
274             }
275         }
276     }
277 
278     private fun writeParameterList(method: MethodItem) {
279         writer.print("(")
280         val emitParameterNames = compatibility.parameterNames
281         method.parameters().asSequence().forEachIndexed { i, parameter ->
282             if (i > 0) {
283                 writer.print(", ")
284             }
285             writeModifiers(parameter)
286             writeType(parameter, parameter.type())
287             if (emitParameterNames) {
288                 val name = parameter.publicName()
289                 if (name != null) {
290                     writer.print(" ")
291                     writer.print(name)
292                 }
293             }
294             if (options.outputDefaultValues && parameter.hasDefaultValue()) {
295                 writer.print(" = ")
296                 val defaultValue = parameter.defaultValue()
297                 if (defaultValue != null) {
298                     writer.print(defaultValue)
299                 } else {
300                     // null is a valid default value!
301                     writer.print("null")
302                 }
303             }
304         }
305         writer.print(")")
306     }
307 
308     private fun writeType(
309         item: Item,
310         type: TypeItem?,
311         outputKotlinStyleNulls: Boolean = options.outputKotlinStyleNulls
312     ) {
313         type ?: return
314 
315         var typeString = type.toTypeString(
316             outerAnnotations = false,
317             innerAnnotations = compatibility.annotationsInSignatures,
318             erased = false,
319             kotlinStyleNulls = outputKotlinStyleNulls,
320             context = item,
321             filter = filterReference
322         )
323 
324         // Strip java.lang. prefix?
325         if (compatibility.omitCommonPackages) {
326             typeString = TypeItem.shortenTypes(typeString)
327         }
328 
329         if (compatibility.includeExtendsObjectInWildcard && typeString.endsWith(", ?>") && item is ParameterItem) {
330             // This wasn't done universally; just in a few places, so replicate it for those exact places
331             val methodName = item.containingMethod().name()
332             when (methodName) {
333                 "computeIfAbsent" -> {
334                     if (typeString == "java.util.function.Function<? super java.lang.Object, ?>") {
335                         typeString = "java.util.function.Function<? super java.lang.Object, ? extends java.lang.Object>"
336                     }
337                 }
338                 "computeIfPresent", "merge", "replaceAll", "compute" -> {
339                     if (typeString == "java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ?>") {
340                         typeString =
341                             "java.util.function.BiFunction<? super java.lang.Object, ? super java.lang.Object, ? extends java.lang.Object>"
342                     }
343                 }
344             }
345         }
346 
347         writer.print(typeString)
348     }
349 
350     private fun writeThrowsList(method: MethodItem) {
351         val throws = when {
352             preFiltered -> method.throwsTypes().asSequence()
353             compatibility.filterThrowsClasses -> method.filteredThrowsTypes(filterReference).asSequence()
354             else -> method.throwsTypes().asSequence()
355         }
356         if (throws.any()) {
357             writer.print(" throws ")
358             throws.asSequence().sortedWith(ClassItem.fullNameComparator).forEachIndexed { i, type ->
359                 if (i > 0) {
360                     writer.print(", ")
361                 }
362                 writer.print(type.qualifiedName())
363             }
364         }
365     }
366 }