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