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
18 
19 import kotlin.reflect.KClass
20 import kotlin.reflect.cast
21 
22 /**
23  * A type safe, immutable set of model options.
24  *
25  * Used to allow a user of a model to pass model specific options through to a model.
26  */
27 class ModelOptions
28 private constructor(
29     /** Description of the model options. */
30     private val description: String,
31     /** The option settings. */
32     private val settings: Map<Key<*>, Any>,
33 ) {
34     /** Get an option value using the type safe [Key]. */
getnull35     operator fun <T : Any> get(key: Key<T>): T {
36         return settings[key]?.let { key.kClass.cast(it) } ?: key.default
37     }
38 
toStringnull39     override fun toString(): String {
40         return description
41     }
42 
isEmptynull43     fun isEmpty(): Boolean {
44         return settings.isEmpty()
45     }
46 
47     /** Builder for [ModelOptionsBuilder]. */
48     class ModelOptionsBuilder internal constructor() {
49         /** The option settings. */
50         internal val settings = mutableMapOf<Key<*>, Any>()
51 
52         /**
53          * Map from [Key.name] to the [Key] used within this set of options. This is used to ensure
54          * that two keys with the same name cannot be used within the same set of options.
55          */
56         private val keys = mutableMapOf<String, Key<*>>()
57 
58         /** Set an option value using the type safe [Key]. */
setnull59         operator fun <T : Any> set(key: Key<T>, value: T) {
60             val existingKey = keys.put(key.name, key)
61             if (existingKey != null)
62                 throw IllegalStateException(
63                     "Duplicate keys with the same name $key and $existingKey"
64                 )
65 
66             settings[key] = value
67         }
68     }
69 
70     companion object {
71         val empty = ModelOptions("empty", emptyMap())
72 
73         /** Build a [ModelOptions]. */
buildnull74         fun build(description: String, block: ModelOptionsBuilder.() -> Unit): ModelOptions {
75             val builder = ModelOptionsBuilder()
76             builder.block()
77             return ModelOptions(description, builder.settings.toMap())
78         }
79     }
80 
81     /** A type safe opaque key. */
82     class Key<T : Any>
83     @PublishedApi
84     internal constructor(
85         internal val name: String,
86         internal val default: T,
87         internal val kClass: KClass<T>
88     ) {
89 
toStringnull90         override fun toString() = "Key($name, $default, $kClass)"
91 
92         companion object {
93             /**
94              * Create a type safe key.
95              *
96              * @param name the name of the key, this should be unique across all key names to allow
97              *   multiple different options from different models to be set together without
98              *   conflict. It is recommended to prefix the name with the name of the model, e.g.
99              *   `model.option`.
100              * @param default the default value of the key.
101              */
102             inline fun <reified T : Any> of(name: String, default: T): Key<T> {
103                 return Key(name, default, T::class)
104             }
105         }
106     }
107 }
108