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 /** 20 * Encapsulates information that metalava needs to know about a specific annotation type. 21 * 22 * Instances of [AnnotationInfo] will be shared across [AnnotationItem]s that have the same 23 * qualified name and (where applicable) the same attributes. That will allow the information in 24 * [AnnotationInfo] to be computed once and then reused whenever needed. 25 * 26 * This class just sets the properties that can be determined simply by looking at the 27 * [qualifiedName]. Any other properties are set to the default, usually `false`. Subclasses can 28 * change that behavior. 29 */ 30 open class AnnotationInfo( 31 /** The fully qualified and normalized name of the annotation class. */ 32 val qualifiedName: String, 33 ) { 34 35 /** 36 * Determines whether the annotation is nullability related. 37 * 38 * If this is null then the annotation is not a nullability annotation, otherwise this 39 * determines whether it is nullable or non-null. 40 */ 41 internal val typeNullability: TypeNullability? = 42 when { 43 isNullableAnnotation(qualifiedName) -> TypeNullability.NULLABLE 44 isNonNullAnnotation(qualifiedName) -> TypeNullability.NONNULL 45 else -> null 46 } 47 48 /** 49 * Determines whether this annotation affects whether the annotated item is shown or hidden and 50 * if so how. 51 */ 52 open val showability: Showability 53 get() = Showability.NO_EFFECT 54 55 open val suppressCompatibility: Boolean 56 get() = false 57 } 58 59 /** 60 * The set of possible effects on whether an `Item` is part of an API. 61 * 62 * They are in order from the lowest priority to the highest priority, see [highestPriority]. 63 */ 64 enum class ShowOrHide(private val show: Boolean?) { 65 /** No effect either way. */ 66 NO_EFFECT(show = null), 67 68 /** Hide an item from the API. */ 69 HIDE(show = false), 70 71 /** Show an item as part of the API. */ 72 SHOW(show = true), 73 74 /** 75 * Revert an unstable API. 76 * 77 * The effect of reverting an unstable API depends on what the previously released API contains 78 * but in the case when the item is new and does not exist in the previously released API 79 * reverting requires hiding the API. As the items being hidden could have show annotations 80 * (which override hide annotations) then in order for the item to be hidden then this needs to 81 * come after [SHOW]. 82 */ 83 REVERT_UNSTABLE_API(show = null) { 84 /** 85 * If the [revertItem] is not null and `emit = true`, i.e. is for the API surface currently 86 * being generated, then reverting will still show this item. 87 */ shownull88 override fun show(revertItem: Item?): Boolean { 89 return revertItem != null && revertItem.emit 90 } 91 92 /** If the [revertItem] is null then reverting will hide this item. */ hidenull93 override fun hide(revertItem: Item?): Boolean { 94 return revertItem == null 95 } 96 }, 97 ; 98 99 /** 100 * Return true if this shows an `Item` as part of the API. 101 * 102 * @param revertItem the optional [Item] in the previously released API to which this will be 103 * reverted. This is only set for, and only has an effect on, [REVERT_UNSTABLE_API], see 104 * [REVERT_UNSTABLE_API.show] for details. 105 */ shownull106 open fun show(revertItem: Item?): Boolean = show == true 107 108 /** 109 * Return true if this hides an `Item` from the API. 110 * 111 * @param revertItem the optional [Item] in the previously released API to which this will be 112 * reverted. This is only set for, and only has an effect on, [REVERT_UNSTABLE_API], see 113 * [REVERT_UNSTABLE_API.show] for details. 114 */ 115 open fun hide(revertItem: Item?): Boolean = show == false 116 117 /** Return the highest priority between this and another [ShowOrHide]. */ 118 fun highestPriority(other: ShowOrHide): ShowOrHide = maxOf(this, other) 119 } 120 121 /** 122 * Determines how an annotation will affect whether [Item]s annotated with it are part of the API or 123 * not and also determines whether an [Item] is part of the API or not. 124 */ 125 data class Showability( 126 /** 127 * Determines whether an API [Item] is shown as part of the API or hidden from the API. 128 * 129 * If [ShowOrHide.show] is `true` then the annotated [Item] will be shown as part of the API. 130 * That is the case for annotations that match `--show-annotation`, or 131 * `--show-single-annotation`, but not `--show-for-stub-purposes-annotation`. 132 * 133 * If [ShowOrHide.hide] is `true` then the annotated [Item] will NOT be shown as part of the 134 * API. That is the case for annotations that match `--hide-annotation`. 135 * 136 * If neither of the above is then this has no effect on whether an annotated [Item] will be 137 * shown or not, that decision will be determined by its container's [Showability.recursive] 138 * setting. 139 */ 140 private val show: ShowOrHide, 141 142 /** 143 * Determines whether the contents of an API [Item] is shown as part of the API or hidden from 144 * the API. 145 * 146 * If [ShowOrHide.show] is `true` then the contents of the annotated [Item] will be included in 147 * the API unless overridden by a closer annotation. That is the case for annotations that match 148 * `--show-annotation`, but not `--show-single-annotation`, or 149 * `--show-for-stub-purposes-annotation`. 150 * 151 * If [ShowOrHide.hide] is `true` then the contents of the annotated [Item] will be included in 152 * the API unless overridden by a closer annotation. That is the case for annotations that match 153 * `--hide-annotation`. 154 */ 155 private val recursive: ShowOrHide, 156 157 /** 158 * Determines whether an API [Item] ands its contents is considered to be part of the base API 159 * and so must be included in the stubs but not the signature files. 160 * 161 * If [ShowOrHide.show] is `true` then the API [Item] ands its contents are considered to be 162 * part of the base API. That is the case for annotations that match 163 * `--show-for-stub-purposes-annotation` but not `--show-annotation`, or 164 * `--show-single-annotation`. 165 */ 166 private val forStubsOnly: ShowOrHide, 167 168 /** The item to which this item should be reverted. Null if no such item exists. */ 169 val revertItem: Item? = null, 170 ) { 171 /** 172 * Check whether the annotated item should be considered part of the API or not. 173 * 174 * Returns `true` if the item is annotated with a `--show-annotation`, 175 * `--show-single-annotation`, or `--show-for-stub-purposes-annotation`. 176 */ 177 fun show() = show.show(revertItem) || forStubsOnly.show(revertItem) 178 179 /** 180 * Check whether the annotated item should only be considered part of the API when generating 181 * stubs. 182 * 183 * Returns `true` if the item is annotated with a `--show-for-stub-purposes-annotation`. Such 184 * items will be part of an API surface that the API being generated extends. 185 */ 186 fun showForStubsOnly() = forStubsOnly.show(revertItem) 187 188 /** 189 * Check whether the annotations on this item only affect the current `Item`. 190 * 191 * Returns `true` if they do, `false` if they can also affect nested `Item`s. 192 */ 193 fun showNonRecursive() = 194 show.show(revertItem) && !recursive.show(revertItem) && !forStubsOnly.show(revertItem) 195 196 /** 197 * Check whether the annotated item should be hidden from the API. 198 * 199 * Returns `true` if the annotation matches an `--hide-annotation`. 200 */ 201 fun hide() = show.hide(revertItem) 202 203 /** 204 * Check whether the annotated item is part of an unstable API that needs to be reverted. 205 * 206 * Returns `true` if the annotation matches `--hide-annotation android.annotation.FlaggedApi` or 207 * if this is on an item then when the item is annotated with such an annotation or is a method 208 * that overrides such an item or is contained within a class that is annotated with such an 209 * annotation. 210 */ 211 fun revertUnstableApi() = show == ShowOrHide.REVERT_UNSTABLE_API 212 213 /** Combine this with [other] to produce a combination [Showability]. */ 214 fun combineWith(other: Showability): Showability { 215 // Show wins over not showing. 216 val newShow = show.highestPriority(other.show) 217 218 // Recursive wins over not recursive. 219 val newRecursive = recursive.highestPriority(other.recursive) 220 221 // For everything wins over only for stubs. 222 val forStubsOnly = 223 if (newShow.show(revertItem)) { 224 ShowOrHide.NO_EFFECT 225 } else { 226 forStubsOnly.highestPriority(other.forStubsOnly) 227 } 228 229 return Showability(newShow, newRecursive, forStubsOnly) 230 } 231 232 companion object { 233 /** The annotation does not affect whether an annotated item is shown. */ 234 val NO_EFFECT = 235 Showability( 236 show = ShowOrHide.NO_EFFECT, 237 recursive = ShowOrHide.NO_EFFECT, 238 forStubsOnly = ShowOrHide.NO_EFFECT 239 ) 240 } 241 } 242