xref: /aosp_15_r20/tools/metalava/metalava-model/src/main/java/com/android/tools/metalava/model/AnnotationManager.kt (revision 115816f9299ab6ddd6b9673b81f34e707f6bacab)
1 /*
2  * Copyright (C) 2023 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 /** Provides support for managing annotations within Metalava. */
20 interface AnnotationManager {
21 
22     /** Get the [AnnotationInfo] for the specified [annotation]. */
getAnnotationInfonull23     fun getAnnotationInfo(annotation: AnnotationItem): AnnotationInfo
24 
25     /**
26      * Maps an annotation name to the name to be used internally.
27      *
28      * Annotations that should not be used internally are mapped to null.
29      */
30     fun normalizeInputName(qualifiedName: String?): String?
31 
32     /**
33      * Maps an annotation name to the name to be used in signatures/stubs/external annotation files.
34      * Annotations that should not be exported are mapped to null.
35      */
36     fun normalizeOutputName(
37         qualifiedName: String?,
38         target: AnnotationTarget = AnnotationTarget.SIGNATURE_FILE
39     ): String?
40 
41     /** Returns true if [annotationName] is the name of one of the show annotations. */
42     fun isShowAnnotationName(annotationName: String): Boolean = false
43 
44     /**
45      * Checks to see if this has any show for stubs purposes annotations.
46      *
47      * Returns true if it has, false otherwise.
48      */
49     fun hasAnyStubPurposesAnnotations(): Boolean = false
50 
51     /**
52      * Get the [Showability] for the supplied [SelectableItem].
53      *
54      * This combines the [Showability] of all the annotations of this item and returns the result.
55      *
56      * If the annotations on the item conflict then this could throw an exception or report an error
57      * as appropriate.
58      */
59     fun getShowabilityForItem(item: SelectableItem): Showability = Showability.NO_EFFECT
60 
61     /**
62      * Checks to see if the modifiers contain any hide annotations.
63      *
64      * See [AnnotationItem.isHideAnnotation]
65      */
66     fun hasHideAnnotations(modifiers: ModifierList): Boolean = false
67 
68     /**
69      * Checks to see if the modifiers contain any suppress compatibility annotations.
70      *
71      * Returns `true` if it does, `false` otherwise. If `true` then the owning item (and any
72      * contents) will have their compatibility checks suppressed but they may still be written to
73      * API files or stub JARs.
74      *
75      * "Suppress compatibility" meta-annotations allow Metalava to handle concepts like Jetpack
76      * experimental APIs, where developers can use the [RequiresOptIn] meta-annotation to mark
77      * feature sets with unstable APIs.
78      */
79     fun hasSuppressCompatibilityMetaAnnotations(modifiers: ModifierList): Boolean = false
80 
81     /** Determine how to handle typedef annotations, i.e. annotations like `@IntDef`. */
82     val typedefMode: TypedefMode
83 }
84 
85 /** Base class for [AnnotationManager] instances. */
86 abstract class BaseAnnotationManager : AnnotationManager {
87 
88     /**
89      * A map from the annotation key (returned by [getKeyForAnnotationItem]) to the corresponding
90      * [AnnotationInfo] (returned by [computeAnnotationInfo]).
91      */
92     private val annotationKeyToInfo = mutableMapOf<String, AnnotationInfo>()
93 
94     override fun getAnnotationInfo(annotation: AnnotationItem): AnnotationInfo {
95         val key = getKeyForAnnotationItem(annotation)
96         val existing = annotationKeyToInfo[key]
97         if (existing != null) {
98             return existing
99         }
100         val info = computeAnnotationInfo(annotation)
101         annotationKeyToInfo[key] = info
102         return info
103     }
104 
105     /**
106      * Construct a key that differentiates between all instances of the annotation class with the
107      * same qualified name as [annotationItem] that have different [AnnotationInfo].
108      *
109      * e.g. if annotation `A()` and `A(value=2)` would both produce the same [AnnotationInfo]
110      * (because the `value` attribute is ignored when computing it) then they should just use `A` as
111      * the key. However, if they would produce different [AnnotationInfo] objects (because the
112      * `value` attribute is used when computing it) then they should create a key that includes the
113      * `value`.
114      *
115      * Note: it is safe to use `annotationItem.qualifiedName!!` as [AnnotationItem.qualifiedName] is
116      * guaranteed not to be `null` when this method is called.
117      */
118     protected abstract fun getKeyForAnnotationItem(annotationItem: AnnotationItem): String
119 
120     /**
121      * Compute an [AnnotationInfo] from the [annotationItem].
122      *
123      * This should only use attributes of the [annotationItem] if they are included in the key
124      * returned by [getKeyForAnnotationItem] for this [annotationItem].
125      *
126      * Note: it is safe to use `annotationItem.qualifiedName!!` as [AnnotationItem.qualifiedName] is
127      * guaranteed not to be `null` when this method is called.
128      */
129     internal abstract fun computeAnnotationInfo(annotationItem: AnnotationItem): AnnotationInfo
130 }
131 
132 /**
133  * A no op implementation of [AnnotationManager] that is suitable for use by the deprecated,
134  * external use only `ApiFile.parseApi(String,String,Boolean?)` and the for test only
135  * `ApiFile.parseApi(String,String,ClassResolver?)` methods.
136  *
137  * This is used when loading an API signature from a text file and makes the following assumptions:
138  * * The annotation names are correct and do not need mapping into another form.
139  * * The annotations can be used in all stubs.
140  */
141 internal class NoOpAnnotationManager : BaseAnnotationManager() {
142 
getKeyForAnnotationItemnull143     override fun getKeyForAnnotationItem(annotationItem: AnnotationItem): String {
144         // Just use the qualified name as the key as [computeAnnotationInfo] does not use anything
145         // else.
146         return annotationItem.qualifiedName
147     }
148 
computeAnnotationInfonull149     override fun computeAnnotationInfo(annotationItem: AnnotationItem): AnnotationInfo {
150         return NoOpAnnotationInfo(annotationItem.qualifiedName)
151     }
152 
normalizeInputNamenull153     override fun normalizeInputName(qualifiedName: String?): String? {
154         return qualifiedName
155     }
156 
normalizeOutputNamenull157     override fun normalizeOutputName(qualifiedName: String?, target: AnnotationTarget): String? {
158         return qualifiedName
159     }
160 
161     override val typedefMode: TypedefMode = TypedefMode.NONE
162 }
163 
164 /**
165  * [AnnotationInfo] implementation used by [NoOpAnnotationManager].
166  *
167  * This class just sets the properties that can be determined simply by looking at the
168  * [qualifiedName]. Any other properties are set to the default, usually `false`.
169  */
170 internal class NoOpAnnotationInfo(
171     /** The fully qualified and normalized name of the annotation class. */
172     val qualifiedName: String,
173 ) : AnnotationInfo {
174 
175     override val targets
176         get() = ANNOTATION_IN_ALL_STUBS
177 
178     override val typeNullability = computeTypeNullability(qualifiedName)
179 
180     override val showability
181         get() = Showability.NO_EFFECT
182 
183     override val suppressCompatibility
184         get() = false
185 }
186 
187 val noOpAnnotationManager: AnnotationManager = NoOpAnnotationManager()
188