1 /*
2  * Copyright (C) 2019 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 android.util;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.TestApi;
22 
23 import java.util.function.Consumer;
24 
25 /**
26  * A sparse array of ArrayMaps, which is suitable for holding (userId, packageName)->object
27  * associations.
28  *
29  * @param <K> Any class
30  * @param <V> Any class
31  * @hide
32  */
33 @TestApi
34 @android.ravenwood.annotation.RavenwoodKeepWholeClass
35 public class SparseArrayMap<K, V> {
36     private final SparseArray<ArrayMap<K, V>> mData = new SparseArray<>();
37 
38     /**
39      * Add an entry associating obj with the int-K pair.
40      *
41      * @return the previous value associated with key, or null if there was no mapping for key.
42      * (A null return can also indicate that the map previously associated null with key, if the
43      * implementation supports null values.)
44      */
add(int key, @NonNull K mapKey, @Nullable V obj)45     public V add(int key, @NonNull K mapKey, @Nullable V obj) {
46         ArrayMap<K, V> data = mData.get(key);
47         if (data == null) {
48             data = new ArrayMap<>();
49             mData.put(key, data);
50         }
51         return data.put(mapKey, obj);
52     }
53 
54     /** Remove all entries from the map. */
clear()55     public void clear() {
56         for (int i = 0; i < mData.size(); ++i) {
57             mData.valueAt(i).clear();
58         }
59     }
60 
61     /** Return true if the structure contains an explicit entry for the int-K pair. */
contains(int key, @NonNull K mapKey)62     public boolean contains(int key, @NonNull K mapKey) {
63         return mData.contains(key) && mData.get(key).containsKey(mapKey);
64     }
65 
66     /** Removes all the data for the key, if there was any. */
delete(int key)67     public void delete(int key) {
68         mData.delete(key);
69     }
70 
71     /**
72      * Removes all the data for the keyIndex, if there was any.
73      * @hide
74      */
deleteAt(int keyIndex)75     public void deleteAt(int keyIndex) {
76         mData.removeAt(keyIndex);
77     }
78 
79     /**
80      * Removes the data for the key and mapKey, if there was any.
81      *
82      * @return Returns the value that was stored under the keys, or null if there was none.
83      */
84     @Nullable
delete(int key, @NonNull K mapKey)85     public V delete(int key, @NonNull K mapKey) {
86         ArrayMap<K, V> data = mData.get(key);
87         if (data != null) {
88             return data.remove(mapKey);
89         }
90         return null;
91     }
92 
93     /**
94      * Removes the data for the keyIndex and mapIndex, if there was any.
95      * @hide
96      */
deleteAt(int keyIndex, int mapIndex)97     public void deleteAt(int keyIndex, int mapIndex) {
98         mData.valueAt(keyIndex).removeAt(mapIndex);
99     }
100 
101     /**
102      * Get the value associated with the int-K pair.
103      */
104     @Nullable
get(int key, @NonNull K mapKey)105     public V get(int key, @NonNull K mapKey) {
106         ArrayMap<K, V> data = mData.get(key);
107         if (data != null) {
108             return data.get(mapKey);
109         }
110         return null;
111     }
112 
113     /**
114      * Returns the value to which the specified key and mapKey are mapped, or defaultValue if this
115      * map contains no mapping for them.
116      */
117     @Nullable
getOrDefault(int key, @NonNull K mapKey, V defaultValue)118     public V getOrDefault(int key, @NonNull K mapKey, V defaultValue) {
119         if (mData.contains(key)) {
120             ArrayMap<K, V> data = mData.get(key);
121             if (data != null && data.containsKey(mapKey)) {
122                 return data.get(mapKey);
123             }
124         }
125         return defaultValue;
126     }
127 
128     /** @see SparseArray#indexOfKey */
indexOfKey(int key)129     public int indexOfKey(int key) {
130         return mData.indexOfKey(key);
131     }
132 
133     /**
134      * Returns the index of the mapKey.
135      *
136      * @see SparseArray#indexOfKey
137      */
indexOfKey(int key, @NonNull K mapKey)138     public int indexOfKey(int key, @NonNull K mapKey) {
139         ArrayMap<K, V> data = mData.get(key);
140         if (data != null) {
141             return data.indexOfKey(mapKey);
142         }
143         return -1;
144     }
145 
146     /** Returns the key at the given index. */
keyAt(int index)147     public int keyAt(int index) {
148         return mData.keyAt(index);
149     }
150 
151     /** Returns the map's key at the given mapIndex for the given keyIndex. */
152     @NonNull
keyAt(int keyIndex, int mapIndex)153     public K keyAt(int keyIndex, int mapIndex) {
154         return mData.valueAt(keyIndex).keyAt(mapIndex);
155     }
156 
157     /** Returns the size of the outer array. */
numMaps()158     public int numMaps() {
159         return mData.size();
160     }
161 
162     /** Returns the number of elements in the map of the given key. */
numElementsForKey(int key)163     public int numElementsForKey(int key) {
164         ArrayMap<K, V> data = mData.get(key);
165         return data == null ? 0 : data.size();
166     }
167 
168     /**
169      * Returns the number of elements in the map of the given keyIndex.
170      * @hide
171      */
numElementsForKeyAt(int keyIndex)172     public int numElementsForKeyAt(int keyIndex) {
173         ArrayMap<K, V> data = mData.valueAt(keyIndex);
174         return data == null ? 0 : data.size();
175     }
176 
177     /** Returns the value V at the given key and map index. */
178     @Nullable
valueAt(int keyIndex, int mapIndex)179     public V valueAt(int keyIndex, int mapIndex) {
180         return mData.valueAt(keyIndex).valueAt(mapIndex);
181     }
182 
183     /** Iterate through all int-K pairs and operate on all of the values. */
forEach(@onNull Consumer<V> consumer)184     public void forEach(@NonNull Consumer<V> consumer) {
185         for (int i = numMaps() - 1; i >= 0; --i) {
186             ArrayMap<K, V> data = mData.valueAt(i);
187             for (int j = data.size() - 1; j >= 0; --j) {
188                 consumer.accept(data.valueAt(j));
189             }
190         }
191     }
192 
193     /**
194      * @param <K> Any class
195      * @param <V> Any class
196      * @hide
197      */
198     public interface TriConsumer<K, V> {
199         /** Consume the int-K-V tuple. */
accept(int key, K mapKey, V value)200         void accept(int key, K mapKey, V value);
201     }
202 
203     /**
204      * Iterate through all int-K pairs and operate on all of the values.
205      * @hide
206      */
forEach(@onNull TriConsumer<K, V> consumer)207     public void forEach(@NonNull TriConsumer<K, V> consumer) {
208         for (int iIdx = numMaps() - 1; iIdx >= 0; --iIdx) {
209             final int i = mData.keyAt(iIdx);
210             final ArrayMap<K, V> data = mData.valueAt(iIdx);
211             for (int kIdx = data.size() - 1; kIdx >= 0; --kIdx) {
212                 consumer.accept(i, data.keyAt(kIdx), data.valueAt(kIdx));
213             }
214         }
215     }
216 }
217