blob: 508cf5c2d1b346117b1bc25571770639a6443d5e [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.util;
import com.google.common.base.Objects;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A {@link Map} that supports multiple values per key.
*/
public class MultiMap<K, V> {
private final Map<K, List<V>> mInternalMap;
public MultiMap() {
mInternalMap = new HashMap<K, List<V>>();
}
/**
* Clears the map.
*/
public void clear() {
mInternalMap.clear();
}
/**
* Checks whether the map contains the specified key.
*
* @see {@link Map#containsKey()}
*/
public boolean containsKey(K key) {
return mInternalMap.containsKey(key);
}
/**
* Checks whether the map contains the specified value.
*
* @see {@link Map#containsValue()}
*/
public boolean containsValue(V value) {
for (List<V> valueList : mInternalMap.values()) {
if (valueList.contains(value)) {
return true;
}
}
return false;
}
/**
* Gets the list of values associated with each key.
*/
public List<V> get(K key) {
return mInternalMap.get(key);
}
/**
* @see {@link Map#isEmpty()}
*/
public boolean isEmpty() {
return mInternalMap.isEmpty();
}
/**
* Check if map is empty.
*/
public Set<K> keySet() {
return mInternalMap.keySet();
}
/**
* Adds the value to the list associated with a key.
*
* @see {@link Map#put()}
*/
public V put(K key, V value) {
List<V> valueList = mInternalMap.get(key);
if (valueList == null) {
valueList = new LinkedList<V>();
mInternalMap.put(key, valueList);
}
valueList.add(value);
return value;
}
/**
*
* Adds all entries in given {@link Map} to this {@link MultiMap}.
*/
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
/**
* Adds all entries in given {@link MultiMap} to this {@link MultiMap}.
*/
public void putAll(MultiMap<K, ? extends V> m) {
for (K key : m.keySet()) {
for (V value : m.get(key)) {
put(key, value);
}
}
}
/**
* Removes all values associated with the specified key.
*/
public List<V> remove(K key) {
return mInternalMap.remove(key);
}
/**
* Returns the number of keys in the map
*/
public int size() {
return mInternalMap.size();
}
/**
* Returns list of all values.
*/
public List<V> values() {
List<V> allValues = new LinkedList<V>();
for (List<V> valueList : mInternalMap.values()) {
allValues.addAll(valueList);
}
return allValues;
}
/**
* Construct a new map, that contains a unique String key for each value.
*
* Current algorithm will construct unique key by appending a unique position number to
* key's toString() value
*
* @return a {@link Map}
*/
public Map<String, V> getUniqueMap() {
Map<String, V> uniqueMap = new HashMap<String, V>();
for (Map.Entry<K, List<V>> entry : mInternalMap.entrySet()) {
int count = 1;
for (V value : entry.getValue()) {
if (count == 1) {
addUniqueEntry(uniqueMap, entry.getKey().toString(), value);
} else {
// append unique number to key for each value
addUniqueEntry(uniqueMap, String.format("%s%d", entry.getKey(), count), value);
}
count++;
}
}
return uniqueMap;
}
/**
* Recursive method that will append characters to proposedKey until its unique. Used in case
* there are collisions with generated key values.
*
* @param uniqueMap
* @param proposedKey
* @param value
*/
private String addUniqueEntry(Map<String, V> uniqueMap, String proposedKey, V value) {
// not the most efficient algorithm, but should work
if (uniqueMap.containsKey(proposedKey)) {
return addUniqueEntry(uniqueMap, String.format("%s%s", proposedKey, "X"), value);
} else {
uniqueMap.put(proposedKey, value);
return proposedKey;
}
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return mInternalMap.hashCode();
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
MultiMap<?, ?> other = (MultiMap<?, ?>) obj;
return Objects.equal(mInternalMap, other.mInternalMap);
}
}