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