1 /*
2  * Copyright 2021 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 package androidx.lifecycle.viewmodel
17 
18 import androidx.lifecycle.ViewModelProvider
19 import androidx.lifecycle.ViewModelProvider.Factory
20 import androidx.lifecycle.viewmodel.CreationExtras.Key
21 import kotlin.jvm.JvmOverloads
22 import kotlin.jvm.JvmStatic
23 
24 /**
25  * A map-like object holding pairs of [CreationExtras.Key] and [Any], enabling efficient value
26  * retrieval for each key. Each key in [CreationExtras] is unique, storing only one value per key.
27  *
28  * [CreationExtras] is used in [ViewModelProvider.Factory.create] to provide extra information to
29  * the [Factory]. This makes [Factory] implementations stateless, simplifying factory injection by
30  * not requiring all information at construction time.
31  *
32  * This abstract class supports read-only access; use [MutableCreationExtras] for read-write access.
33  */
34 public abstract class CreationExtras internal constructor() {
35     internal val extras: MutableMap<Key<*>, Any?> = mutableMapOf()
36 
37     /**
38      * Key for the elements of [CreationExtras]. [T] represents the type of element associated with
39      * this key.
40      */
41     public interface Key<T>
42 
43     /**
44      * Returns the value to which the specified [key] is associated, or null if this
45      * [CreationExtras] contains no mapping for the key.
46      */
getnull47     public abstract operator fun <T> get(key: Key<T>): T?
48 
49     /** Compares the specified object with this [CreationExtras] for equality. */
50     override fun equals(other: Any?): Boolean = other is CreationExtras && extras == other.extras
51 
52     /** Returns the hash code value for this [CreationExtras]. */
53     override fun hashCode(): Int = extras.hashCode()
54 
55     /**
56      * Returns a string representation of this [CreationExtras]. The string representation consists
57      * of a list of key-value mappings in the order returned by the [CreationExtras]'s iterator.
58      */
59     override fun toString(): String = "CreationExtras(extras=$extras)"
60 
61     /** An empty read-only [CreationExtras]. */
62     public object Empty : CreationExtras() {
63         override fun <T> get(key: Key<T>): T? = null
64     }
65 
66     public companion object {
67         /** Returns an unique [Key] to be associated with an extra. */
Keynull68         @JvmStatic public inline fun <reified T> Key(): Key<T> = object : Key<T> {}
69     }
70 }
71 
72 /**
73  * A modifiable [CreationExtras] that holds pairs of [CreationExtras.Key] and [Any], allowing
74  * efficient value retrieval for each key.
75  *
76  * Each key in [CreationExtras] is unique, storing only one value per key.
77  *
78  * @see [CreationExtras]
79  */
80 public class MutableCreationExtras
81 /**
82  * Constructs a [MutableCreationExtras] containing the elements of the specified `initialExtras`, in
83  * the order they are returned by the [Map]'s iterator.
84  */
85 internal constructor(initialExtras: Map<Key<*>, Any?>) : CreationExtras() {
86 
87     /**
88      * Constructs a [MutableCreationExtras] containing the elements of the specified
89      * [initialExtras], in the order they are returned by the [CreationExtras]'s iterator.
90      */
91     @JvmOverloads
92     public constructor(initialExtras: CreationExtras = Empty) : this(initialExtras.extras)
93 
94     init {
95         extras += initialExtras
96     }
97 
98     /** Associates the specified [t] with the specified [key] in this [CreationExtras]. */
setnull99     public operator fun <T> set(key: Key<T>, t: T) {
100         extras[key] = t
101     }
102 
103     /**
104      * Returns the value to which the specified [key] is associated, or null if this
105      * [CreationExtras] contains no mapping for the key.
106      */
getnull107     @Suppress("UNCHECKED_CAST") public override fun <T> get(key: Key<T>): T? = extras[key] as T?
108 }
109 
110 /**
111  * Checks if the [CreationExtras] contains the given [key].
112  *
113  * This method allows to use the `key in creationExtras` syntax for checking whether an [key] is
114  * contained in the [CreationExtras].
115  */
116 public operator fun CreationExtras.contains(key: Key<*>): Boolean = key in extras
117 
118 /**
119  * Creates a new read-only [CreationExtras] by replacing or adding entries to [this] extras from
120  * another [creationExtras].
121  *
122  * The returned [CreationExtras] preserves the entry iteration order of the original
123  * [CreationExtras].
124  *
125  * Those entries of another [creationExtras] that are missing in [this] extras are iterated in the
126  * end in the order of that [creationExtras].
127  */
128 public operator fun CreationExtras.plus(creationExtras: CreationExtras): MutableCreationExtras =
129     MutableCreationExtras(initialExtras = extras + creationExtras.extras)
130 
131 /** Appends or replaces all entries from the given [creationExtras] in [this] mutable extras. */
132 public operator fun MutableCreationExtras.plusAssign(creationExtras: CreationExtras) {
133     extras += creationExtras.extras
134 }
135