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.server.permission.access.immutable
18 
19 /**
20  * Wrapper class for reference to a mutable data structure instance.
21  *
22  * This class encapsulates the logic to mutate/copy a mutable data structure instance and update the
23  * reference to the new mutated instance. It also remembers the mutated instance so that it can be
24  * reused during further mutations.
25  *
26  * Instances of this class should be kept private within a data structure, with the [get] method
27  * exposed on the immutable interface of the data structure as a `getFoo` method, and the [mutate]
28  * method exposed on the mutable interface of the data structure as a `mutateFoo` method. When the
29  * data structure is mutated/copied, a new instance of this class should be obtained with
30  * [toImmutable], which makes the wrapped reference immutable-only again and thus prevents further
31  * modifications to a data structure accessed with its immutable interface.
32  *
33  * @see MutableIndexedReferenceMap
34  * @see MutableIntReferenceMap
35  */
36 class MutableReference<I : Immutable<M>, M : I>
37 private constructor(private var immutable: I, private var mutable: M?) {
38     constructor(mutable: M) : this(mutable, mutable)
39 
40     /** Return an immutable reference to the wrapped mutable data structure. */
getnull41     fun get(): I = immutable
42 
43     /**
44      * Make the wrapped mutable data structure mutable, by either calling [Immutable.toMutable] and
45      * replacing the wrapped reference with its result, or reusing the existing reference if it's
46      * already mutable.
47      */
48     fun mutate(): M {
49         mutable?.let {
50             return it
51         }
52         return immutable.toMutable().also {
53             immutable = it
54             mutable = it
55         }
56     }
57 
58     /**
59      * Create a new [MutableReference] instance with the wrapped mutable data structure being
60      * immutable-only again.
61      */
toImmutablenull62     fun toImmutable(): MutableReference<I, M> = MutableReference(immutable, null)
63 
64     override fun equals(other: Any?): Boolean {
65         if (this === other) {
66             return true
67         }
68         if (javaClass != other?.javaClass) {
69             return false
70         }
71         other as MutableReference<*, *>
72         return immutable == other.immutable
73     }
74 
hashCodenull75     override fun hashCode(): Int = immutable.hashCode()
76 }
77