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 <T> Any class
30  * @hide
31  */
32 @TestApi
33 public class SparseArrayMap<T> {
34     private final SparseArray<ArrayMap<String, T>> mData = new SparseArray<>();
35 
36     /** Add an entry associating obj with the int-String pair. */
add(int key, @NonNull String mapKey, @Nullable T obj)37     public void add(int key, @NonNull String mapKey, @Nullable T obj) {
38         ArrayMap<String, T> data = mData.get(key);
39         if (data == null) {
40             data = new ArrayMap<>();
41             mData.put(key, data);
42         }
43         data.put(mapKey, obj);
44     }
45 
46     /** Remove all entries from the map. */
clear()47     public void clear() {
48         for (int i = 0; i < mData.size(); ++i) {
49             mData.valueAt(i).clear();
50         }
51     }
52 
53     /** Return true if the structure contains an explicit entry for the int-String pair. */
contains(int key, @NonNull String mapKey)54     public boolean contains(int key, @NonNull String mapKey) {
55         return mData.contains(key) && mData.get(key).containsKey(mapKey);
56     }
57 
58     /** Removes all the data for the key, if there was any. */
delete(int key)59     public void delete(int key) {
60         mData.delete(key);
61     }
62 
63     /**
64      * Removes the data for the key and mapKey, if there was any.
65      *
66      * @return Returns the value that was stored under the keys, or null if there was none.
67      */
68     @Nullable
delete(int key, @NonNull String mapKey)69     public T delete(int key, @NonNull String mapKey) {
70         ArrayMap<String, T> data = mData.get(key);
71         if (data != null) {
72             return data.remove(mapKey);
73         }
74         return null;
75     }
76 
77     /**
78      * Get the value associated with the int-String pair.
79      */
80     @Nullable
get(int key, @NonNull String mapKey)81     public T get(int key, @NonNull String mapKey) {
82         ArrayMap<String, T> data = mData.get(key);
83         if (data != null) {
84             return data.get(mapKey);
85         }
86         return null;
87     }
88 
89     /**
90      * Returns the value to which the specified key and mapKey are mapped, or defaultValue if this
91      * map contains no mapping for them.
92      */
93     @Nullable
getOrDefault(int key, @NonNull String mapKey, T defaultValue)94     public T getOrDefault(int key, @NonNull String mapKey, T defaultValue) {
95         if (mData.contains(key)) {
96             ArrayMap<String, T> data = mData.get(key);
97             if (data != null && data.containsKey(mapKey)) {
98                 return data.get(mapKey);
99             }
100         }
101         return defaultValue;
102     }
103 
104     /** @see SparseArray#indexOfKey */
indexOfKey(int key)105     public int indexOfKey(int key) {
106         return mData.indexOfKey(key);
107     }
108 
109     /**
110      * Returns the index of the mapKey.
111      *
112      * @see SparseArray#indexOfKey
113      */
indexOfKey(int key, @NonNull String mapKey)114     public int indexOfKey(int key, @NonNull String mapKey) {
115         ArrayMap<String, T> data = mData.get(key);
116         if (data != null) {
117             return data.indexOfKey(mapKey);
118         }
119         return -1;
120     }
121 
122     /** Returns the key at the given index. */
keyAt(int index)123     public int keyAt(int index) {
124         return mData.keyAt(index);
125     }
126 
127     /** Returns the map's key at the given mapIndex for the given keyIndex. */
128     @NonNull
keyAt(int keyIndex, int mapIndex)129     public String keyAt(int keyIndex, int mapIndex) {
130         return mData.valueAt(keyIndex).keyAt(mapIndex);
131     }
132 
133     /** Returns the size of the outer array. */
numMaps()134     public int numMaps() {
135         return mData.size();
136     }
137 
138     /** Returns the number of elements in the map of the given key. */
numElementsForKey(int key)139     public int numElementsForKey(int key) {
140         ArrayMap<String, T> data = mData.get(key);
141         return data == null ? 0 : data.size();
142     }
143 
144     /** Returns the value T at the given key and map index. */
145     @Nullable
valueAt(int keyIndex, int mapIndex)146     public T valueAt(int keyIndex, int mapIndex) {
147         return mData.valueAt(keyIndex).valueAt(mapIndex);
148     }
149 
150     /** Iterate through all int-String pairs and operate on all of the values. */
forEach(@onNull Consumer<T> consumer)151     public void forEach(@NonNull Consumer<T> consumer) {
152         for (int i = numMaps() - 1; i >= 0; --i) {
153             ArrayMap<String, T> data = mData.valueAt(i);
154             for (int j = data.size() - 1; j >= 0; --j) {
155                 consumer.accept(data.valueAt(j));
156             }
157         }
158     }
159 }
160