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