xref: /aosp_15_r20/tools/metalava/metalava-model/src/main/java/com/android/tools/metalava/model/api/surface/ApiVariant.kt (revision 115816f9299ab6ddd6b9673b81f34e707f6bacab)
1 /*
2  * Copyright (C) 2024 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.api.surface
18 
19 /** An API variant of [type] for [surface] */
20 class ApiVariant(
21     /** The [ApiSurface] of which this is a variant. */
22     val surface: ApiSurface,
23 
24     /** The type of this variant. */
25     val type: ApiVariantType,
26 
27     /**
28      * The list of all [ApiVariant]s belonging to the owning [ApiSurfaces].
29      *
30      * This must add itself to it.
31      */
32     allVariants: MutableList<ApiVariant>,
33 ) {
34     /**
35      * Bit mask for this, used within [ApiVariantSet].
36      *
37      * This must be unique across all [ApiVariant]s within `allVariants` so it computes the bit
38      * based on the current size of `allVariants` and then adds itself to the list ensuring that the
39      * next [ApiVariant] will use a different bit.
40      */
<lambda>null41     internal val bitMask: Int = 1 shl allVariants.size.also { allVariants.add(this) }
42 
toStringnull43     override fun toString(): String {
44         return "${surface.name}(${type.name})"
45     }
46 }
47 
48 /**
49  * The base set of [ApiVariant]s.
50  *
51  * Provides common query only functionality for [ApiVariantSet] and [MutableApiVariantSet].
52  */
53 sealed class BaseApiVariantSet(internal val apiSurfaces: ApiSurfaces) {
54     internal abstract val bits: Int
55 
isEmptynull56     fun isEmpty() = bits == 0
57 
58     fun isNotEmpty() = bits != 0
59 
60     operator fun contains(variant: ApiVariant) = (bits and variant.bitMask) != 0
61 
62     /** True if this set contains any of the variants from [surface]. */
63     fun containsAny(surface: ApiSurface) = containsAny(surface.variantSet)
64 
65     /** True if this set contains any of the variants from [variantSet]. */
66     fun containsAny(variantSet: ApiVariantSet): Boolean {
67         require(apiSurfaces === variantSet.apiSurfaces) {
68             "Mismatch between ApiSurfaces, this set is for $apiSurfaces, other set is for ${variantSet.apiSurfaces}"
69         }
70         return (bits and variantSet.bits) != 0
71     }
72 
73     /**
74      * Get a [MutableApiVariantSet] from this.
75      *
76      * This will return the object on which it is called if that is already mutable, otherwise it
77      * will create a separate mutable copy of this.
78      */
toMutablenull79     abstract fun toMutable(): MutableApiVariantSet
80 
81     /**
82      * Get an immutable [ApiVariantSet] from this.
83      *
84      * This will return the object on which it is called if that is already immutable, otherwise it
85      * will create a separate immutable copy of this.
86      */
87     abstract fun toImmutable(): ApiVariantSet
88 
89     override fun equals(other: Any?): Boolean {
90         if (this === other) return true
91         if (other !is BaseApiVariantSet) return false
92 
93         if (apiSurfaces != other.apiSurfaces) return false
94         if (bits != other.bits) return false
95 
96         return true
97     }
98 
hashCodenull99     override fun hashCode(): Int {
100         var result = apiSurfaces.hashCode()
101         result = 31 * result + bits
102         return result
103     }
104 
toStringnull105     override fun toString(): String {
106         return buildString {
107             append("ApiVariantSet[")
108             var separator = ""
109             for (apiSurface in apiSurfaces.all) {
110                 // If this set does not contain any variants from the ApiSurface then ignore it.
111                 if (!this@BaseApiVariantSet.containsAny(apiSurface)) continue
112                 append(separator)
113                 separator = ","
114                 append(apiSurface.name)
115                 append("(")
116                 for (variant in apiSurface.variants) {
117                     if (variant in this@BaseApiVariantSet) append(variant.type.shortCode)
118                 }
119                 append(")")
120             }
121             append("]")
122         }
123     }
124 }
125 
126 /** An immutable set of [ApiVariant]s. */
127 class ApiVariantSet(apiSurfaces: ApiSurfaces, override val bits: Int) :
128     BaseApiVariantSet(apiSurfaces) {
129 
toMutablenull130     override fun toMutable() = MutableApiVariantSet(apiSurfaces, bits)
131 
132     override fun toImmutable() = this
133 
134     companion object {
135         internal fun emptySet(apiSurfaces: ApiSurfaces) = ApiVariantSet(apiSurfaces, 0)
136 
137         /**
138          * Build an [ApiVariantSet].
139          *
140          * Creates a [MutableApiVariantSet], calls [lambda] to modify it and then calls
141          * [MutableApiVariantSet.toImmutable] to return an immutable [ApiVariantSet].
142          *
143          * @param apiSurfaces the [ApiSurfaces] whose [ApiVariant]s it will contain.
144          * @param lambda the lambda that will be passed a [MutableApiVariantSet] to modify.
145          */
146         fun build(apiSurfaces: ApiSurfaces, lambda: MutableApiVariantSet.() -> Unit) =
147             MutableApiVariantSet(apiSurfaces).apply(lambda).toImmutable()
148     }
149 }
150 
151 /** A mutable set of [ApiVariant]s. */
152 class MutableApiVariantSet
153 internal constructor(apiSurfaces: ApiSurfaces, override var bits: Int = 0) :
154     BaseApiVariantSet(apiSurfaces) {
155 
toMutablenull156     override fun toMutable() = this
157 
158     override fun toImmutable() =
159         if (bits == 0) apiSurfaces.emptyVariantSet else ApiVariantSet(apiSurfaces, bits)
160 
161     /**
162      * Add [variant] to this set.
163      *
164      * This has no effect if it is already a member.
165      */
166     fun add(variant: ApiVariant) {
167         bits = bits or variant.bitMask
168     }
169 
170     /**
171      * Remove [variant] from this set.
172      *
173      * This has no effect if it was not a member.
174      */
removenull175     fun remove(variant: ApiVariant) {
176         bits = bits and variant.bitMask.inv()
177     }
178 
179     /** Clear the set. */
clearnull180     fun clear() {
181         bits = 0
182     }
183 
184     companion object {
185 
186         /** Create a [MutableApiVariantSet] for [apiSurfaces]. */
setOfnull187         fun setOf(apiSurfaces: ApiSurfaces): MutableApiVariantSet {
188             // Make sure all the variant bits can fit into an Int.
189             if (apiSurfaces.variants.count() > 30)
190                 error("Too many API variants to store in the set")
191             return MutableApiVariantSet(apiSurfaces, 0)
192         }
193     }
194 }
195