<lambda>null1 package com.android.tools.metalava
2 
3 import com.android.SdkConstants.ATTR_VALUE
4 import com.android.tools.metalava.model.AnnotationAttribute
5 import com.android.tools.metalava.model.AnnotationItem
6 import com.android.tools.metalava.model.DefaultAnnotationAttribute
7 
8 interface AnnotationFilter {
9     // tells whether an annotation is included by the filter
10     fun matches(annotation: AnnotationItem): Boolean
11     // tells whether an annotation is included by this filter
12     fun matches(annotationSource: String): Boolean
13 
14     // Returns a list of fully qualified annotation names that may be included by this filter.
15     // Note that this filter might incorporate parameters but this function strips them.
16     fun getIncludedAnnotationNames(): List<String>
17     // Tells whether there exists an annotation that is accepted by this filter and that
18     // ends with the given suffix
19     fun matchesSuffix(annotationSuffix: String): Boolean
20     // Returns true if nothing is matched by this filter
21     fun isEmpty(): Boolean
22     // Returns true if some annotation is matched by this filter
23     fun isNotEmpty(): Boolean
24     // Returns the fully-qualified class name of the first annotation matched by this filter
25     fun firstQualifiedName(): String
26 }
27 
28 // Mutable implementation of AnnotationFilter
29 class MutableAnnotationFilter : AnnotationFilter {
30     private val inclusionExpressions = mutableListOf<AnnotationFilterEntry>()
31 
32     // Adds the given source as a fully qualified annotation name to match with this filter
33     // Can be "androidx.annotation.RestrictTo"
34     // Can be "androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP)"
35     // Note that the order of calls to this method could affect the return from
36     // {@link #firstQualifiedName} .
addnull37     fun add(source: String) {
38         inclusionExpressions.add(AnnotationFilterEntry.fromSource(source))
39     }
40 
matchesnull41     override fun matches(annotationSource: String): Boolean {
42         val annotationText = annotationSource.replace("@", "")
43         val wrapper = AnnotationFilterEntry.fromSource(annotationText)
44         return matches(wrapper)
45     }
46 
matchesnull47     override fun matches(annotation: AnnotationItem): Boolean {
48         if (annotation.qualifiedName() == null) {
49             return false
50         }
51         val wrapper = AnnotationFilterEntry.fromAnnotationItem(annotation)
52         return matches(wrapper)
53     }
54 
matchesnull55     private fun matches(annotation: AnnotationFilterEntry): Boolean {
56         return inclusionExpressions.any { includedAnnotation ->
57             annotationsMatch(includedAnnotation, annotation)
58         }
59     }
60 
getIncludedAnnotationNamesnull61     override fun getIncludedAnnotationNames(): List<String> {
62         val annotationNames = mutableListOf<String>()
63         for (expression in inclusionExpressions) {
64             annotationNames.add(expression.qualifiedName)
65         }
66         return annotationNames
67     }
68 
matchesSuffixnull69     override fun matchesSuffix(annotationSuffix: String): Boolean {
70         return inclusionExpressions.any { included ->
71             included.qualifiedName.endsWith(annotationSuffix)
72         }
73     }
74 
isEmptynull75     override fun isEmpty(): Boolean {
76         return inclusionExpressions.isEmpty()
77     }
78 
isNotEmptynull79     override fun isNotEmpty(): Boolean {
80         return !isEmpty()
81     }
82 
firstQualifiedNamenull83     override fun firstQualifiedName(): String {
84         val inclusion = inclusionExpressions.first()
85         return inclusion.qualifiedName
86     }
87 
annotationsMatchnull88     private fun annotationsMatch(filter: AnnotationFilterEntry, existingAnnotation: AnnotationFilterEntry): Boolean {
89         if (filter.qualifiedName != existingAnnotation.qualifiedName) {
90             return false
91         }
92         if (filter.attributes.count() > existingAnnotation.attributes.count()) {
93             return false
94         }
95         for (attribute in filter.attributes) {
96             val existingValue = existingAnnotation.findAttribute(attribute.name)?.value?.toSource()
97             if (existingValue != attribute.value.toSource()) {
98                 return false
99             }
100         }
101         return true
102     }
103 }
104 
105 // An AnnotationFilterEntry filters for annotations having a certain qualifiedName and
106 // possibly certain attributes.
107 // An AnnotationFilterEntry doesn't necessarily have a Codebase like an AnnotationItem does
108 private class AnnotationFilterEntry(
109     val qualifiedName: String,
110     val attributes: List<AnnotationAttribute>
111 ) {
findAttributenull112     fun findAttribute(name: String?): AnnotationAttribute? {
113         val actualName = name ?: ATTR_VALUE
114         return attributes.firstOrNull { it.name == actualName }
115     }
116 
117     companion object {
fromSourcenull118         fun fromSource(source: String): AnnotationFilterEntry {
119             val text = source.replace("@", "")
120             val index = text.indexOf("(")
121 
122             val qualifiedName = if (index == -1) {
123                 text
124             } else {
125                 text.substring(0, index)
126             }
127 
128             val attributes: List<AnnotationAttribute> = if (index == -1) {
129                 emptyList()
130             } else {
131                 DefaultAnnotationAttribute.createList(
132                     text.substring(index + 1, text.lastIndexOf(')'))
133                 )
134             }
135             return AnnotationFilterEntry(qualifiedName, attributes)
136         }
137 
fromAnnotationItemnull138         fun fromAnnotationItem(annotationItem: AnnotationItem): AnnotationFilterEntry {
139             // Have to call toSource to resolve attribute values into fully qualified class names.
140             // For example: resolving RestrictTo(LIBRARY_GROUP) into
141             // RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP)
142             // In addition, toSource (with the default argument showDefaultAttrs=true) retrieves
143             // default attributes from the definition of the annotation. For example,
144             // @SystemApi actually is converted into @android.annotation.SystemApi(\
145             // client=android.annotation.SystemApi.Client.PRIVILEGED_APPS,\
146             // process=android.annotation.SystemApi.Process.ALL)
147             return AnnotationFilterEntry.fromSource(annotationItem.toSource())
148         }
149     }
150 }
151