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.model
18 
19 import com.android.tools.metalava.DocLevel
20 import com.android.tools.metalava.DocLevel.HIDDEN
21 import com.android.tools.metalava.DocLevel.PACKAGE
22 import com.android.tools.metalava.DocLevel.PRIVATE
23 import com.android.tools.metalava.DocLevel.PROTECTED
24 import com.android.tools.metalava.DocLevel.PUBLIC
25 import com.android.tools.metalava.Options
26 import com.android.tools.metalava.compatibility
27 import com.android.tools.metalava.options
28 import java.io.Writer
29 
30 interface ModifierList {
31     val codebase: Codebase
32     fun annotations(): List<AnnotationItem>
33 
34     fun owner(): Item
35     fun getVisibilityLevel(): VisibilityLevel
36     fun isPublic(): Boolean
37     fun isProtected(): Boolean
38     fun isPrivate(): Boolean
39     fun isStatic(): Boolean
40     fun isAbstract(): Boolean
41     fun isFinal(): Boolean
42     fun isNative(): Boolean
43     fun isSynchronized(): Boolean
44     fun isStrictFp(): Boolean
45     fun isTransient(): Boolean
46     fun isVolatile(): Boolean
47     fun isDefault(): Boolean
48 
49     // Modifier in Kotlin, separate syntax (...) in Java but modeled as modifier here
50     fun isVarArg(): Boolean = false
51 
52     // Kotlin
53     fun isSealed(): Boolean = false
54     fun isCompanion(): Boolean = false
55     fun isInfix(): Boolean = false
56     fun isConst(): Boolean = false
57     fun isSuspend(): Boolean = false
58     fun isOperator(): Boolean = false
59     fun isInline(): Boolean = false
60     fun isEmpty(): Boolean
61 
62     fun isPackagePrivate() = !(isPublic() || isProtected() || isPrivate())
63     fun isPublicOrProtected() = isPublic() || isProtected()
64 
65     // Rename? It's not a full equality, it's whether an override's modifier set is significant
66     fun equivalentTo(other: ModifierList): Boolean {
67         if (isPublic() != other.isPublic()) return false
68         if (isProtected() != other.isProtected()) return false
69         if (isPrivate() != other.isPrivate()) return false
70 
71         if (isStatic() != other.isStatic()) return false
72         if (isAbstract() != other.isAbstract()) return false
73         if (isFinal() != other.isFinal()) { return false }
74         if (compatibility.includeSynchronized && isSynchronized() != other.isSynchronized()) return false
75         if (isTransient() != other.isTransient()) return false
76         if (isVolatile() != other.isVolatile()) return false
77 
78         // Default does not require an override to "remove" it
79         // if (isDefault() != other.isDefault()) return false
80 
81         return true
82     }
83 
84     /** Returns true if this modifier list contains any nullness information */
85     fun hasNullnessInfo(): Boolean {
86         return annotations().any { it.isNonNull() || it.isNullable() }
87     }
88 
89     /** Returns true if this modifier list contains any a Nullable annotation */
90     fun isNullable(): Boolean {
91         return annotations().any { it.isNullable() }
92     }
93 
94     /**
95      * Returns true if this modifier list contains any annotations explicitly passed in
96      * via [Options.showAnnotations]
97      */
98     fun hasShowAnnotation(): Boolean {
99         if (options.showAnnotations.isEmpty()) {
100             return false
101         }
102         return annotations().any {
103             options.showAnnotations.matches(it)
104         }
105     }
106 
107     /**
108      * Returns true if this modifier list contains any annotations explicitly passed in
109      * via [Options.showSingleAnnotations]
110      */
111     fun hasShowSingleAnnotation(): Boolean {
112 
113         if (options.showSingleAnnotations.isEmpty()) {
114             return false
115         }
116         return annotations().any {
117             options.showSingleAnnotations.matches(it)
118         }
119     }
120 
121     /**
122      * Returns true if this modifier list contains any annotations explicitly passed in
123      * via [Options.showForStubPurposesAnnotations]
124      */
125     fun hasShowForStubPurposesAnnotation(): Boolean {
126         if (options.showForStubPurposesAnnotations.isEmpty()) {
127             return false
128         }
129         return annotations().any {
130             options.showForStubPurposesAnnotations.matches(it)
131         }
132     }
133 
134     /**
135      * Returns true if this modifier list contains any annotations explicitly passed in
136      * via [Options.hideAnnotations] or any annotations which are themselves annotated
137      * with meta-annotations explicitly passed in via [Options.hideMetaAnnotations]
138      *
139      * @see hasHideMetaAnnotations
140      */
141     fun hasHideAnnotations(): Boolean {
142         if (options.hideAnnotations.isEmpty() && options.hideMetaAnnotations.isEmpty()) {
143             return false
144         }
145         return annotations().any { annotation ->
146             options.hideAnnotations.matches(annotation) ||
147                 annotation.resolve()?.hasHideMetaAnnotation() ?: false
148         }
149     }
150 
151     /**
152      * Returns true if this modifier list contains any meta-annotations explicitly passed in
153      * via [Options.hideMetaAnnotations].
154      *
155      * Hidden meta-annotations allow Metalava to handle concepts like Kotlin's [Experimental],
156      * which allows developers to create annotations that describe experimental features -- sets
157      * of distinct and potentially overlapping unstable API surfaces. Libraries may wish to exclude
158      * such sets of APIs from tracking and stub JAR generation by passing [Experimental] as a
159      * hidden meta-annotation.
160      */
161     fun hasHideMetaAnnotations(): Boolean {
162         if (options.hideMetaAnnotations.isEmpty()) {
163             return false
164         }
165         return annotations().any { annotation ->
166             options.hideMetaAnnotations.contains(annotation.qualifiedName())
167         }
168     }
169 
170     /** Returns true if this modifier list contains the given annotation */
171     fun isAnnotatedWith(qualifiedName: String): Boolean {
172         return findAnnotation(qualifiedName) != null
173     }
174 
175     /** Returns the annotation of the given qualified name if found in this modifier list */
176     fun findAnnotation(qualifiedName: String): AnnotationItem? {
177         val mappedName = AnnotationItem.mapName(codebase, qualifiedName)
178         return annotations().firstOrNull {
179             mappedName == it.qualifiedName()
180         }
181     }
182 
183     /** Returns true if this modifier list has adequate access */
184     fun checkLevel() = checkLevel(options.docLevel)
185 
186     /**
187      * Returns true if this modifier list has access modifiers that
188      * are adequate for the given documentation level
189      */
190     fun checkLevel(level: DocLevel): Boolean {
191         if (level == HIDDEN) {
192             return true
193         } else if (owner().isHiddenOrRemoved()) {
194             return false
195         }
196         return when (level) {
197             PUBLIC -> isPublic()
198             PROTECTED -> isPublic() || isProtected()
199             PACKAGE -> !isPrivate()
200             PRIVATE, HIDDEN -> true
201         }
202     }
203 
204     /**
205      * Returns true if the visibility modifiers in this modifier list is as least as visible
206      * as the ones in the given [other] modifier list
207      */
208     fun asAccessibleAs(other: ModifierList): Boolean {
209         val otherLevel = other.getVisibilityLevel()
210         val thisLevel = getVisibilityLevel()
211         // Generally the access level enum order determines relative visibility. However, there is an exception because
212         // package private and internal are not directly comparable.
213         val result = thisLevel >= otherLevel
214         return when (otherLevel) {
215             VisibilityLevel.PACKAGE_PRIVATE -> result && thisLevel != VisibilityLevel.INTERNAL
216             VisibilityLevel.INTERNAL -> result && thisLevel != VisibilityLevel.PACKAGE_PRIVATE
217             else -> result
218         }
219     }
220 
221     /** User visible description of the visibility in this modifier list */
222     fun getVisibilityString(): String {
223         return getVisibilityLevel().userVisibleDescription
224     }
225 
226     /**
227      * Like [getVisibilityString], but package private has no modifiers; this typically corresponds to
228      * the source code for the visibility modifiers in the modifier list
229      */
230     fun getVisibilityModifiers(): String {
231         return getVisibilityLevel().javaSourceCodeModifier
232     }
233 
234     companion object {
235         fun write(
236             writer: Writer,
237             modifiers: ModifierList,
238             item: Item,
239             target: AnnotationTarget,
240             // TODO: "deprecated" isn't a modifier; clarify method name
241             includeDeprecated: Boolean = false,
242             includeAnnotations: Boolean = true,
243             runtimeAnnotationsOnly: Boolean = false,
244             skipNullnessAnnotations: Boolean = false,
245             omitCommonPackages: Boolean = false,
246             removeAbstract: Boolean = false,
247             removeFinal: Boolean = false,
248             addPublic: Boolean = false,
249             separateLines: Boolean = false,
250             language: Language = Language.JAVA
251         ) {
252 
253             val list = if (removeAbstract || removeFinal || addPublic) {
254                 class AbstractFiltering : ModifierList by modifiers {
255                     override fun isAbstract(): Boolean {
256                         return if (removeAbstract) false else modifiers.isAbstract()
257                     }
258 
259                     override fun isFinal(): Boolean {
260                         return if (removeFinal) false else modifiers.isFinal()
261                     }
262 
263                     override fun getVisibilityLevel(): VisibilityLevel {
264                         return if (addPublic) VisibilityLevel.PUBLIC else modifiers.getVisibilityLevel()
265                     }
266                 }
267                 AbstractFiltering()
268             } else {
269                 modifiers
270             }
271 
272             if (includeAnnotations) {
273                 writeAnnotations(
274                     item,
275                     target,
276                     runtimeAnnotationsOnly,
277                     includeDeprecated,
278                     writer,
279                     separateLines,
280                     list,
281                     skipNullnessAnnotations,
282                     omitCommonPackages
283                 )
284             } else {
285                 // We always include @Deprecated annotation in stub files
286                 if (item.deprecated && target.isStubsFile()) {
287                     writer.write("@Deprecated")
288                     writer.write(if (separateLines) "\n" else " ")
289                 }
290             }
291 
292             if (item is PackageItem) {
293                 // Packages use a modifier list, but only annotations apply
294                 return
295             }
296 
297             // Kotlin order:
298             //   https://kotlinlang.org/docs/reference/coding-conventions.html#modifiers
299 
300             // Abstract: should appear in interfaces if in compat mode
301             val classItem = item as? ClassItem
302             val methodItem = item as? MethodItem
303 
304             // Order based on the old stubs code: TODO, use Java standard order instead?
305 
306             if (compatibility.nonstandardModifierOrder) {
307                 val visibilityLevel = list.getVisibilityLevel()
308                 if (visibilityLevel != VisibilityLevel.PACKAGE_PRIVATE) {
309                     writer.write(visibilityLevel.javaSourceCodeModifier + " ")
310                 }
311 
312                 if (list.isDefault()) {
313                     writer.write("default ")
314                 }
315 
316                 if (list.isStatic() && (compatibility.staticEnums || classItem == null || !classItem.isEnum())) {
317                     writer.write("static ")
318                 }
319 
320                 if (list.isFinal() &&
321                     // Don't show final on parameters: that's an implementation side detail
322                     item !is ParameterItem &&
323                     (classItem?.isEnum() != true || compatibility.finalInInterfaces) ||
324                     compatibility.forceFinalInEnumValueMethods &&
325                     methodItem?.name() == "values" && methodItem.containingClass().isEnum()
326                 ) {
327                     writer.write("final ")
328                 }
329 
330                 if (list.isSealed()) {
331                     writer.write("sealed ")
332                 }
333 
334                 if (list.isSuspend()) {
335                     writer.write("suspend ")
336                 }
337 
338                 if (list.isInline()) {
339                     writer.write("inline ")
340                 }
341 
342                 if (list.isInfix()) {
343                     writer.write("infix ")
344                 }
345 
346                 if (list.isOperator()) {
347                     writer.write("operator ")
348                 }
349 
350                 val isInterface = classItem?.isInterface() == true ||
351                     (methodItem?.containingClass()?.isInterface() == true &&
352                         !list.isDefault() && !list.isStatic())
353 
354                 if ((compatibility.abstractInInterfaces && isInterface ||
355                         list.isAbstract() &&
356                         (classItem?.isEnum() != true &&
357                             (compatibility.abstractInAnnotations || classItem?.isAnnotationType() != true))) &&
358                     (!isInterface || compatibility.abstractInInterfaces)
359                 ) {
360                     writer.write("abstract ")
361                 }
362 
363                 if (list.isNative() && target.isStubsFile()) {
364                     writer.write("native ")
365                 }
366 
367                 if (item.deprecated && includeDeprecated && !target.isStubsFile() && !compatibility.deprecatedAsAnnotation) {
368                     writer.write("deprecated ")
369                 }
370 
371                 if (list.isSynchronized() && (compatibility.includeSynchronized || target.isStubsFile())) {
372                     writer.write("synchronized ")
373                 }
374 
375                 if (list.isTransient()) {
376                     writer.write("transient ")
377                 }
378 
379                 if (list.isVolatile()) {
380                     writer.write("volatile ")
381                 }
382             } else {
383                 if (item.deprecated && includeDeprecated && !target.isStubsFile() && !compatibility.deprecatedAsAnnotation) {
384                     writer.write("deprecated ")
385                 }
386 
387                 val visibilityLevel = list.getVisibilityLevel()
388                 val modifier = if (language == Language.JAVA) {
389                     visibilityLevel.javaSourceCodeModifier
390                 } else {
391                     visibilityLevel.kotlinSourceCodeModifier
392                 }
393                 if (modifier.isNotEmpty()) {
394                     writer.write("$modifier ")
395                 }
396 
397                 val isInterface = classItem?.isInterface() == true ||
398                     (methodItem?.containingClass()?.isInterface() == true &&
399                         !list.isDefault() && !list.isStatic())
400 
401                 if ((compatibility.abstractInInterfaces && isInterface ||
402                         list.isAbstract() &&
403                         (classItem?.isEnum() != true &&
404                             (compatibility.abstractInAnnotations || classItem?.isAnnotationType() != true))) &&
405                     (!isInterface || compatibility.abstractInInterfaces)
406                 ) {
407                     writer.write("abstract ")
408                 }
409 
410                 if (list.isDefault() && item !is ParameterItem) {
411                     writer.write("default ")
412                 }
413 
414                 if (list.isStatic() && (compatibility.staticEnums || classItem == null || !classItem.isEnum())) {
415                     writer.write("static ")
416                 }
417 
418                 if (list.isFinal() &&
419                     language == Language.JAVA &&
420                     // Don't show final on parameters: that's an implementation side detail
421                     item !is ParameterItem &&
422                     (classItem?.isEnum() != true || compatibility.finalInInterfaces)
423                 ) {
424                     writer.write("final ")
425                 } else if (!list.isFinal() && language == Language.KOTLIN) {
426                     writer.write("open ")
427                 }
428 
429                 if (list.isSealed()) {
430                     writer.write("sealed ")
431                 }
432 
433                 if (list.isSuspend()) {
434                     writer.write("suspend ")
435                 }
436 
437                 if (list.isInline()) {
438                     writer.write("inline ")
439                 }
440 
441                 if (list.isInfix()) {
442                     writer.write("infix ")
443                 }
444 
445                 if (list.isOperator()) {
446                     writer.write("operator ")
447                 }
448 
449                 if (list.isTransient()) {
450                     writer.write("transient ")
451                 }
452 
453                 if (list.isVolatile()) {
454                     writer.write("volatile ")
455                 }
456 
457                 if (list.isSynchronized() && (compatibility.includeSynchronized || target.isStubsFile())) {
458                     writer.write("synchronized ")
459                 }
460 
461                 if (list.isNative() && target.isStubsFile()) {
462                     writer.write("native ")
463                 }
464             }
465         }
466 
467         fun writeAnnotations(
468             item: Item,
469             target: AnnotationTarget,
470             runtimeAnnotationsOnly: Boolean,
471             includeDeprecated: Boolean,
472             writer: Writer,
473             separateLines: Boolean,
474             list: ModifierList,
475             skipNullnessAnnotations: Boolean,
476             omitCommonPackages: Boolean
477         ) {
478             //  if includeDeprecated we want to do it
479             //  unless runtimeOnly is false, in which case we'd include it too
480             // e.g. emit @Deprecated if includeDeprecated && !runtimeOnly
481             if (item.deprecated &&
482                 (compatibility.deprecatedAsAnnotation || target.isStubsFile()) &&
483                 (runtimeAnnotationsOnly || includeDeprecated)
484             ) {
485                 writer.write("@Deprecated")
486                 writer.write(if (separateLines) "\n" else " ")
487             }
488 
489             writeAnnotations(
490                 list = list,
491                 runtimeAnnotationsOnly = runtimeAnnotationsOnly,
492                 skipNullnessAnnotations = skipNullnessAnnotations,
493                 omitCommonPackages = omitCommonPackages,
494                 separateLines = separateLines,
495                 writer = writer,
496                 target = target
497             )
498         }
499 
500         fun writeAnnotations(
501             list: ModifierList,
502             skipNullnessAnnotations: Boolean = false,
503             runtimeAnnotationsOnly: Boolean = false,
504             omitCommonPackages: Boolean = false,
505             separateLines: Boolean = false,
506             filterDuplicates: Boolean = false,
507             writer: Writer,
508             target: AnnotationTarget
509         ) {
510             var annotations = list.annotations()
511 
512             // Ensure stable signature file order
513             if (annotations.size > 1) {
514                 annotations = annotations.sortedBy { it.qualifiedName() }
515             }
516 
517             if (annotations.isNotEmpty()) {
518                 var index = -1
519                 for (annotation in annotations) {
520                     index++
521 
522                     if (runtimeAnnotationsOnly && annotation.retention != AnnotationRetention.RUNTIME) {
523                         continue
524                     }
525 
526                     var printAnnotation = annotation
527                     if (!annotation.targets().contains(target)) {
528                         continue
529                     } else if ((annotation.isNullnessAnnotation())) {
530                         if (skipNullnessAnnotations) {
531                             continue
532                         }
533                     } else if (annotation.qualifiedName() == "java.lang.Deprecated") {
534                         // Special cased in stubs and signature files: emitted first
535                         continue
536                     } else if (options.typedefMode == Options.TypedefMode.INLINE) {
537                         val typedef = annotation.findTypedefAnnotation()
538                         if (typedef != null) {
539                             printAnnotation = typedef
540                         }
541                     } else if (options.typedefMode == Options.TypedefMode.REFERENCE &&
542                         annotation.targets() === ANNOTATION_SIGNATURE_ONLY &&
543                         annotation.findTypedefAnnotation() != null) {
544                         // For annotation references, only include the simple name
545                         writer.write("@")
546                         writer.write(annotation.resolve()?.simpleName() ?: annotation.qualifiedName()!!)
547                         if (separateLines) {
548                             writer.write("\n")
549                         } else {
550                             writer.write(" ")
551                         }
552                         continue
553                     }
554 
555                     // Optionally filter out duplicates
556                     if (index > 0 && filterDuplicates) {
557                         val qualifiedName = annotation.qualifiedName()
558                         var found = false
559                         for (i in 0 until index) {
560                             val prev = annotations[i]
561                             if (prev.qualifiedName() == qualifiedName) {
562                                 found = true
563                                 break
564                             }
565                         }
566                         if (found) {
567                             continue
568                         }
569                     }
570 
571                     val source = printAnnotation.toSource(target, showDefaultAttrs = false)
572 
573                     if (omitCommonPackages) {
574                         writer.write(AnnotationItem.shortenAnnotation(source))
575                     } else {
576                         writer.write(source)
577                     }
578                     if (separateLines) {
579                         writer.write("\n")
580                     } else {
581                         writer.write(" ")
582                     }
583                 }
584             }
585         }
586     }
587 }
588