package org.ovirt.engine.core.utils.collections; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.TransformerUtils; /** * A map decorator for providing copies of keys and values upon invocation of accessor methods. With this decorator the * caller will be able to safely update the retrieved objects without worrying that the objects held by the map are * altered (as he will get copies and not references to the objects held by the map). * * @param <K> * @param <V> */ public class CopyOnAccessMap<K, V> implements Map<K, V> { private static final int REALLOCATION_FACTOR = 2; private Map<K, V> innerMap; Transformer cloner; public CopyOnAccessMap(Map<K, V> innerMap) { this.innerMap = innerMap; cloner = TransformerUtils.cloneTransformer(); } @Override public int size() { return innerMap.size(); } @Override public boolean isEmpty() { return innerMap.isEmpty(); } @Override public boolean containsKey(Object key) { return innerMap.containsKey(key); } @Override public boolean containsValue(Object value) { return innerMap.containsValue(value); } @Override public V get(Object key) { return clone(innerMap.get(key)); } @SuppressWarnings("unchecked") private <O> O clone(O originalKey) { return (O) cloner.transform(originalKey); } @Override public V put(K key, V value) { //The old value is no longer in the map, so no need to protect it from external modifiers, so it is not cloned return innerMap.put(clone(key), clone(value)); } @Override public V remove(Object key) { return innerMap.remove(key); } @Override public void putAll(Map<? extends K, ? extends V> m) { for (Entry<? extends K, ? extends V> entry : m.entrySet()) { put(entry.getKey(), entry.getValue()); } } @Override public void clear() { innerMap.clear(); } @Override public Set<K> keySet() { Set<K> newSet = new HashSet<K>(innerMap.size() * REALLOCATION_FACTOR); for (K key : innerMap.keySet()) { newSet.add(clone(key)); } return newSet; } @Override public Collection<V> values() { List<V> newValues = new ArrayList<V>(innerMap.size() * REALLOCATION_FACTOR); for (V value : innerMap.values()) { newValues.add(clone(value)); } return newValues; } @Override public Set<java.util.Map.Entry<K, V>> entrySet() { Map<K, V> newHashMap = new HashMap<K, V>(innerMap.size() * REALLOCATION_FACTOR); for (Entry<K, V> entry : innerMap.entrySet()) { newHashMap.put(clone(entry.getKey()), clone(entry.getValue())); } return newHashMap.entrySet(); } }