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 /** Get the applicable targets for the annotation */ 42 fun computeTargets( 43 annotation: AnnotationItem, 44 classFinder: (String) -> ClassItem? 45 ): Set<AnnotationTarget> 46 47 /** Returns true if [annotationName] is the name of one of the show annotations. */ 48 fun isShowAnnotationName(annotationName: String): Boolean = false 49 50 /** 51 * Checks to see if this has any show for stubs purposes annotations. 52 * 53 * Returns true if it has, false otherwise. 54 */ 55 fun hasAnyStubPurposesAnnotations(): Boolean = false 56 57 /** 58 * Get the [Showability] for the supplied [Item]. 59 * 60 * This combines the [Showability] of all the annotations of this item and returns the result. 61 * 62 * If the annotations on the item conflict then this could throw an exception or report an error 63 * as appropriate. 64 */ 65 fun getShowabilityForItem(item: Item): Showability = Showability.NO_EFFECT 66 67 /** 68 * Checks to see if the modifiers contain any hide annotations. 69 * 70 * See [AnnotationItem.isHideAnnotation] 71 */ 72 fun hasHideAnnotations(modifiers: ModifierList): Boolean = false 73 74 /** 75 * Checks to see if the modifiers contain any suppress compatibility annotations. 76 * 77 * Returns `true` if it does, `false` otherwise. If `true` then the owning item (and any 78 * contents) will have their compatibility checks suppressed but they may still be written to 79 * API files or stub JARs. 80 * 81 * "Suppress compatibility" meta-annotations allow Metalava to handle concepts like Jetpack 82 * experimental APIs, where developers can use the [RequiresOptIn] meta-annotation to mark 83 * feature sets with unstable APIs. 84 */ 85 fun hasSuppressCompatibilityMetaAnnotations(modifiers: ModifierList): Boolean = false 86 87 /** Determine how to handle typedef annotations, i.e. annotations like `@IntDef`. */ 88 val typedefMode: TypedefMode 89 } 90 91 /** 92 * The default empty [AnnotationInfo] used when a more applicable one cannot be created, e.g. when 93 * the [AnnotationItem.qualifiedName] is `null`. 94 */ 95 private val noInfoAvailable = AnnotationInfo(qualifiedName = "") 96 97 /** Base class for [AnnotationManager] instances. */ 98 abstract class BaseAnnotationManager : AnnotationManager { 99 100 /** 101 * A map from the annotation key (returned by [getKeyForAnnotationItem]) to the corresponding 102 * [AnnotationInfo] (returned by [computeAnnotationInfo]). 103 */ 104 private val annotationKeyToInfo = mutableMapOf<String, AnnotationInfo>() 105 106 override fun getAnnotationInfo(annotation: AnnotationItem): AnnotationInfo { 107 annotation.qualifiedName ?: return noInfoAvailable 108 val key = getKeyForAnnotationItem(annotation) 109 val existing = annotationKeyToInfo[key] 110 if (existing != null) { 111 return existing 112 } 113 val info = computeAnnotationInfo(annotation) 114 annotationKeyToInfo[key] = info 115 return info 116 } 117 118 /** 119 * Construct a key that differentiates between all instances of the annotation class with the 120 * same qualified name as [annotationItem] that have different [AnnotationInfo]. 121 * 122 * e.g. if annotation `A()` and `A(value=2)` would both produce the same [AnnotationInfo] 123 * (because the `value` attribute is ignored when computing it) then they should just use `A` as 124 * the key. However, if they would produce different [AnnotationInfo] objects (because the 125 * `value` attribute is used when computing it) then they should create a key that includes the 126 * `value`. 127 * 128 * Note: it is safe to use `annotationItem.qualifiedName!!` as [AnnotationItem.qualifiedName] is 129 * guaranteed not to be `null` when this method is called. 130 */ 131 protected abstract fun getKeyForAnnotationItem(annotationItem: AnnotationItem): String 132 133 /** 134 * Compute an [AnnotationInfo] from the [annotationItem]. 135 * 136 * This should only use attributes of the [annotationItem] if they are included in the key 137 * returned by [getKeyForAnnotationItem] for this [annotationItem]. 138 * 139 * Note: it is safe to use `annotationItem.qualifiedName!!` as [AnnotationItem.qualifiedName] is 140 * guaranteed not to be `null` when this method is called. 141 */ 142 protected abstract fun computeAnnotationInfo(annotationItem: AnnotationItem): AnnotationInfo 143 } 144 145 /** 146 * A no op implementation of [AnnotationManager] that is suitable for use by the deprecated, 147 * external use only `ApiFile.parseApi(String,String,Boolean?)` and the for test only 148 * `ApiFile.parseApi(String,String,ClassResolver?)` methods. 149 * 150 * This is used when loading an API signature from a text file and makes the following assumptions: 151 * * The annotation names are correct and do not need mapping into another form. 152 * * The annotations can be used in all stubs. 153 */ 154 internal class NoOpAnnotationManager : BaseAnnotationManager() { 155 getKeyForAnnotationItemnull156 override fun getKeyForAnnotationItem(annotationItem: AnnotationItem): String { 157 // Just use the qualified name as the key as [computeAnnotationInfo] does not use anything 158 // else. 159 return annotationItem.qualifiedName!! 160 } 161 computeAnnotationInfonull162 override fun computeAnnotationInfo(annotationItem: AnnotationItem): AnnotationInfo { 163 return AnnotationInfo(annotationItem.qualifiedName!!) 164 } 165 normalizeInputNamenull166 override fun normalizeInputName(qualifiedName: String?): String? { 167 return qualifiedName 168 } 169 normalizeOutputNamenull170 override fun normalizeOutputName(qualifiedName: String?, target: AnnotationTarget): String? { 171 return qualifiedName 172 } 173 computeTargetsnull174 override fun computeTargets( 175 annotation: AnnotationItem, 176 classFinder: (String) -> ClassItem? 177 ): Set<AnnotationTarget> = ANNOTATION_IN_ALL_STUBS 178 179 override val typedefMode: TypedefMode = TypedefMode.NONE 180 } 181 182 val noOpAnnotationManager: AnnotationManager = NoOpAnnotationManager() 183