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