• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 com.android.tradefed.util;
17 
18 import com.android.tradefed.build.BuildSerializedVersion;
19 
20 import com.google.common.base.Objects;
21 
22 import java.io.Serializable;
23 import java.util.AbstractMap;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 
33 /** A {@link Map} that supports multiple values per key. */
34 public class MultiMap<K, V> implements Serializable {
35 
36     private static final long serialVersionUID = BuildSerializedVersion.VERSION;
37     private final Map<K, List<V>> mInternalMap;
38 
MultiMap()39     public MultiMap() {
40         mInternalMap = new HashMap<K, List<V>>();
41     }
42 
MultiMap(MultiMap<K, V> map)43     public MultiMap(MultiMap<K, V> map) {
44         this();
45         for (K key : map.keySet()) {
46             List<V> value = map.get(key);
47             mInternalMap.put(key, value);
48         }
49     }
50 
51     /**
52      * Clears the map.
53      */
clear()54     public void clear() {
55         mInternalMap.clear();
56     }
57 
58     /**
59      * Checks whether the map contains the specified key.
60      *
61      * @see Map#containsKey(Object)
62      */
containsKey(K key)63     public boolean containsKey(K key) {
64         return mInternalMap.containsKey(key);
65     }
66 
67     /**
68      * Checks whether the map contains the specified value.
69      *
70      * @see Map#containsValue(Object)
71      */
containsValue(V value)72     public boolean containsValue(V value) {
73         for (List<V> valueList : mInternalMap.values()) {
74             if (valueList.contains(value)) {
75                 return true;
76             }
77         }
78         return false;
79     }
80 
81     /**
82      * Gets the list of values associated with each key.
83      */
get(K key)84     public List<V> get(K key) {
85         return mInternalMap.get(key);
86     }
87 
88     /**
89      * @see Map#isEmpty()
90      */
isEmpty()91     public boolean isEmpty() {
92         return mInternalMap.isEmpty();
93     }
94 
95     /** Returns a collection of all distinct keys contained in this multimap. */
keySet()96     public Set<K> keySet() {
97         return mInternalMap.keySet();
98     }
99 
100     /**
101      * Returns a collection of all key-value pairs in this MultiMap as <code>Map.Entry</code>
102      * instances.
103      */
entries()104     public Collection<Map.Entry<K, V>> entries() {
105         ArrayList<Map.Entry<K, V>> entries = new ArrayList<>();
106         for (K key : keySet()) {
107             for (V value : get(key)) {
108                 entries.add(new AbstractMap.SimpleImmutableEntry<>(key, value));
109             }
110         }
111         return Collections.unmodifiableList(entries);
112     }
113 
114     /**
115      * Adds the value to the list associated with a key.
116      *
117      * @see Map#put(Object, Object)
118      */
put(K key, V value)119     public V put(K key, V value) {
120         List<V> valueList = mInternalMap.get(key);
121         if (valueList == null) {
122             valueList = new LinkedList<V>();
123             mInternalMap.put(key, valueList);
124         }
125         valueList.add(value);
126         return value;
127     }
128 
129     /**
130      *
131      * Adds all entries in given {@link Map} to this {@link MultiMap}.
132      */
putAll(Map<? extends K, ? extends V> m)133     public void putAll(Map<? extends K, ? extends V> m) {
134         for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
135             put(entry.getKey(), entry.getValue());
136         }
137     }
138 
139    /**
140     * Adds all entries in given {@link MultiMap} to this {@link MultiMap}.
141     */
putAll(MultiMap<K, ? extends V> m)142    public void putAll(MultiMap<K, ? extends V> m) {
143        for (K key : m.keySet()) {
144            for (V value : m.get(key)) {
145                put(key, value);
146            }
147        }
148    }
149 
150     /**
151      * Removes all values associated with the specified key.
152      */
remove(K key)153     public List<V> remove(K key) {
154         return mInternalMap.remove(key);
155     }
156 
157     /**
158      * Returns the number of keys in the map
159      */
size()160     public int size() {
161         return mInternalMap.size();
162     }
163 
164     /**
165      * Returns list of all values.
166      */
values()167     public List<V> values() {
168         List<V> allValues = new LinkedList<V>();
169         for (List<V> valueList : mInternalMap.values()) {
170             allValues.addAll(valueList);
171         }
172         return allValues;
173     }
174 
175     /**
176      * Construct a new map, that contains a unique String key for each value.
177      *
178      * Current algorithm will construct unique key by appending a unique position number to
179      * key's toString() value
180      *
181      * @return a {@link Map}
182      */
getUniqueMap()183     public Map<String, V> getUniqueMap() {
184         Map<String, V> uniqueMap = new HashMap<String, V>();
185         for (Map.Entry<K, List<V>> entry : mInternalMap.entrySet()) {
186             int count = 1;
187             for (V value : entry.getValue()) {
188                 if (count == 1) {
189                     addUniqueEntry(uniqueMap, entry.getKey().toString(), value);
190                 } else {
191                     // append unique number to key for each value
192                     addUniqueEntry(uniqueMap, String.format("%s%d", entry.getKey(), count), value);
193                 }
194                 count++;
195             }
196         }
197         return uniqueMap;
198     }
199 
200     /**
201      * Recursive method that will append characters to proposedKey until its unique. Used in case
202      * there are collisions with generated key values.
203      *
204      * @param uniqueMap
205      * @param proposedKey
206      * @param value
207      */
addUniqueEntry(Map<String, V> uniqueMap, String proposedKey, V value)208     private String addUniqueEntry(Map<String, V> uniqueMap, String proposedKey, V value) {
209         // not the most efficient algorithm, but should work
210         if (uniqueMap.containsKey(proposedKey)) {
211             return addUniqueEntry(uniqueMap, String.format("%s%s", proposedKey, "X"), value);
212         } else {
213             uniqueMap.put(proposedKey, value);
214             return proposedKey;
215         }
216     }
217 
218     /**
219      * {@inheritDoc}
220      */
221     @Override
hashCode()222     public int hashCode() {
223         return mInternalMap.hashCode();
224     }
225 
226     /**
227      * {@inheritDoc}
228      */
229     @Override
equals(Object obj)230     public boolean equals(Object obj) {
231         if (this == obj) {
232             return true;
233         }
234         if (obj == null) {
235             return false;
236         }
237         if (getClass() != obj.getClass()) {
238             return false;
239         }
240         MultiMap<?, ?> other = (MultiMap<?, ?>) obj;
241         return Objects.equal(mInternalMap, other.mInternalMap);
242     }
243 }
244