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