• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2018 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.CallableItem
20 import com.android.tools.metalava.model.ClassItem
21 import com.android.tools.metalava.model.Codebase
22 import com.android.tools.metalava.model.ConstructorItem
23 import com.android.tools.metalava.model.DelegatedVisitor
24 import com.android.tools.metalava.model.ExceptionTypeItem
25 import com.android.tools.metalava.model.FieldItem
26 import com.android.tools.metalava.model.Item
27 import com.android.tools.metalava.model.JAVA_LANG_ANNOTATION
28 import com.android.tools.metalava.model.JAVA_LANG_ENUM
29 import com.android.tools.metalava.model.MethodItem
30 import com.android.tools.metalava.model.PackageItem
31 import com.android.tools.metalava.model.PropertyItem
32 import com.android.tools.metalava.model.TypeItem
33 import com.android.tools.metalava.model.psi.CodePrinter
34 import com.android.tools.metalava.model.visitors.ApiFilters
35 import com.android.tools.metalava.model.visitors.ApiVisitor
36 import com.android.tools.metalava.model.visitors.FilteringApiVisitor
37 import com.android.utils.XmlUtils
38 import java.io.PrintWriter
39 
40 /**
41  * Writes out an XML format in the JDiff schema: See $ANDROID/external/jdiff/src/api.xsd (though
42  * limited to the same subset as generated by Doclava; and using the same conventions for the
43  * unspecified parts of the schema, such as what value to put in the deprecated string. It also uses
44  * the same XML formatting.)
45  *
46  * Known differences: Doclava seems to skip enum fields. We don't do that. Doclava seems to skip
47  * type parameters; we do the same.
48  */
49 class JDiffXmlWriter(
50     private val writer: PrintWriter,
51     private val apiName: String? = null,
52 ) : DelegatedVisitor {
53 
54     override fun visitCodebase(codebase: Codebase) {
55         writer.print("<api")
56 
57         if (apiName != null) {
58             // See JDiff's XMLToAPI#nameAPI
59             writer.print(" name=\"")
60             writer.print(apiName)
61             writer.print("\"")
62         }
63 
64         // Specify metalava schema used for metalava:enumConstant
65         writer.print(" xmlns:metalava=\"http://www.android.com/metalava/\"")
66 
67         writer.println(">")
68     }
69 
70     override fun afterVisitCodebase(codebase: Codebase) {
71         writer.println("</api>")
72     }
73 
74     override fun visitPackage(pkg: PackageItem) {
75         // Note: we apparently don't write package annotations anywhere
76         writer.println("<package name=\"${pkg.qualifiedName()}\"\n>")
77     }
78 
79     override fun afterVisitPackage(pkg: PackageItem) {
80         writer.println("</package>")
81     }
82 
83     override fun visitClass(cls: ClassItem) {
84         writer.print('<')
85         // XML format does not seem to special case annotations or enums
86         if (cls.isInterface()) {
87             writer.print("interface")
88         } else {
89             writer.print("class")
90         }
91         writer.print(" name=\"")
92         writer.print(cls.fullName())
93         // Note - to match doclava we don't write out the type parameter list
94         // (cls.typeParameterList()) in JDiff files!
95         writer.print("\"")
96 
97         writeSuperClassAttribute(cls)
98 
99         val modifiers = cls.modifiers
100         writer.print("\n abstract=\"")
101         writer.print(modifiers.isAbstract())
102         writer.print("\"\n static=\"")
103         writer.print(modifiers.isStatic())
104         writer.print("\"\n final=\"")
105         writer.print(modifiers.isFinal())
106         writer.print("\"\n deprecated=\"")
107         writer.print(deprecation(cls))
108         writer.print("\"\n visibility=\"")
109         writer.print(modifiers.getVisibilityModifiers())
110         writer.println("\"\n>")
111 
112         writeInterfaceList(cls)
113     }
114 
115     fun deprecation(item: Item): String {
116         return if (item.originallyDeprecated) {
117             "deprecated"
118         } else {
119             "not deprecated"
120         }
121     }
122 
123     override fun afterVisitClass(cls: ClassItem) {
124         writer.print("</")
125         if (cls.isInterface()) {
126             writer.print("interface")
127         } else {
128             writer.print("class")
129         }
130         writer.println(">")
131     }
132 
133     override fun visitConstructor(constructor: ConstructorItem) {
134         val modifiers = constructor.modifiers
135         writer.print("<constructor name=\"")
136         writer.print(constructor.containingClass().fullName())
137         writer.print("\"\n type=\"")
138         writer.print(constructor.containingClass().qualifiedName())
139         writer.print("\"\n static=\"")
140         writer.print(modifiers.isStatic())
141         writer.print("\"\n final=\"")
142         writer.print(modifiers.isFinal())
143         writer.print("\"\n deprecated=\"")
144         writer.print(deprecation(constructor))
145         writer.print("\"\n visibility=\"")
146         writer.print(modifiers.getVisibilityModifiers())
147         writer.println("\"\n>")
148 
149         // Note - to match doclava we don't write out the type parameter list
150         // (constructor.typeParameterList()) in JDiff files!
151 
152         writeParameterList(constructor)
153         writeThrowsList(constructor)
154         writer.println("</constructor>")
155     }
156 
157     override fun visitField(field: FieldItem) {
158         val modifiers = field.modifiers
159         val initialValue = field.legacyInitialValue(true)
160         val value =
161             if (initialValue != null) {
162                 XmlUtils.toXmlAttributeValue(CodePrinter.constantToSource(initialValue))
163             } else null
164 
165         writer.print("<field name=\"")
166         writer.print(field.name())
167         writer.print("\"\n type=\"")
168         writer.print(XmlUtils.toXmlAttributeValue(formatType(field.type())))
169         writer.print("\"\n transient=\"")
170         writer.print(modifiers.isTransient())
171         writer.print("\"\n volatile=\"")
172         writer.print(modifiers.isVolatile())
173         if (value != null) {
174             writer.print("\"\n value=\"")
175             writer.print(value)
176         }
177 
178         writer.print("\"\n static=\"")
179         writer.print(modifiers.isStatic())
180         writer.print("\"\n final=\"")
181         writer.print(modifiers.isFinal())
182         writer.print("\"\n deprecated=\"")
183         writer.print(deprecation(field))
184         writer.print("\"\n visibility=\"")
185         writer.print(modifiers.getVisibilityModifiers())
186         writer.print("\"")
187         if (field.isEnumConstant()) {
188             // Metalava extension. JDiff doesn't support it.
189             writer.print("\n metalava:enumConstant=\"true\"")
190         }
191         writer.println("\n>\n</field>")
192     }
193 
194     override fun visitProperty(property: PropertyItem) {
195         // Not supported by JDiff
196     }
197 
198     override fun visitMethod(method: MethodItem) {
199         val modifiers = method.modifiers
200 
201         // Note - to match doclava we don't write out the type parameter list
202         // (method.typeParameterList()) in JDiff files!
203 
204         writer.print("<method name=\"")
205         writer.print(method.name())
206         method.returnType().let {
207             writer.print("\"\n return=\"")
208             writer.print(XmlUtils.toXmlAttributeValue(formatType(it)))
209         }
210         writer.print("\"\n abstract=\"")
211         writer.print(modifiers.isAbstract())
212         writer.print("\"\n native=\"")
213         writer.print(modifiers.isNative())
214         writer.print("\"\n synchronized=\"")
215         writer.print(modifiers.isSynchronized())
216         writer.print("\"\n static=\"")
217         writer.print(modifiers.isStatic())
218         writer.print("\"\n final=\"")
219         writer.print(modifiers.isFinal())
220         writer.print("\"\n deprecated=\"")
221         writer.print(deprecation(method))
222         writer.print("\"\n visibility=\"")
223         writer.print(modifiers.getVisibilityModifiers())
224         writer.println("\"\n>")
225 
226         writeParameterList(method)
227         writeThrowsList(method)
228         writer.println("</method>")
229     }
230 
231     private fun writeSuperClassAttribute(cls: ClassItem) {
232         val superClass = cls.superClassType()
233         val superClassString =
234             when {
235                 cls.isAnnotationType() -> JAVA_LANG_ANNOTATION
236                 superClass != null -> {
237                     // doclava seems to include java.lang.Object for classes but not interfaces
238                     if (!cls.isClass() && superClass.isJavaLangObject()) {
239                         return
240                     }
241                     XmlUtils.toXmlAttributeValue(formatType(superClass.toTypeString()))
242                 }
243                 cls.isEnum() -> JAVA_LANG_ENUM
244                 else -> return
245             }
246         writer.print("\n extends=\"")
247         writer.print(superClassString)
248         writer.print("\"")
249     }
250 
251     private fun writeInterfaceList(cls: ClassItem) {
252         val interfaces = cls.interfaceTypes()
253         if (interfaces.isNotEmpty()) {
254             interfaces.forEach { item ->
255                 writer.print("<implements name=\"")
256                 val type = item.toTypeString()
257                 writer.print(XmlUtils.toXmlAttributeValue(formatType(type)))
258                 writer.println("\">\n</implements>")
259             }
260         }
261     }
262 
263     private fun writeParameterList(callable: CallableItem) {
264         callable.parameters().asSequence().forEach { parameter ->
265             // NOTE: We report parameter name as "null" rather than the real name to match
266             // doclava's behavior
267             writer.print("<parameter name=\"null\" type=\"")
268             writer.print(XmlUtils.toXmlAttributeValue(formatType(parameter.type())))
269             writer.println("\">")
270             writer.println("</parameter>")
271         }
272     }
273 
274     private fun formatType(type: TypeItem): String = formatType(type.toTypeString())
275 
276     private fun formatType(typeString: String): String {
277         // In JDiff we always want to include spaces after commas; the API signature tests depend
278         // on this.
279         return typeString.replace(",", ", ").replace(",  ", ", ")
280     }
281 
282     private fun writeThrowsList(callable: CallableItem) {
283         val throws = callable.throwsTypes()
284         if (throws.isNotEmpty()) {
285             throws.sortedWith(ExceptionTypeItem.fullNameComparator).forEach { type ->
286                 writer.print("<exception name=\"")
287                 @Suppress("DEPRECATION") writer.print(type.fullName())
288                 writer.print("\" type=\"")
289                 writer.print(type.toTypeString())
290                 writer.println("\">")
291                 writer.println("</exception>")
292             }
293         }
294     }
295 }
296 
297 /**
298  * Create an [ApiVisitor] that will filter the [Item] to which is applied according to the supplied
299  * parameters and in a manner appropriate for writing JDiff files, e.g. not nesting classes. It will
300  * delegate any visitor calls that pass through its filter to [delegate].
301  */
createFilteringVisitorForJDiffWriternull302 fun createFilteringVisitorForJDiffWriter(
303     delegate: DelegatedVisitor,
304     apiFilters: ApiFilters,
305     preFiltered: Boolean,
306     showUnannotated: Boolean,
307     filterSuperClassType: Boolean = true,
308 ): ApiVisitor =
309     FilteringApiVisitor(
310         delegate,
311         inlineInheritedFields = true,
312         interfaceListComparator = TypeItem.totalComparator,
313         apiFilters = apiFilters,
314         preFiltered = preFiltered,
315         filterSuperClassType = filterSuperClassType,
316         showUnannotated = showUnannotated,
317     )
318