package tc.oc.commons.core.util; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.ForwardingMap; import com.google.common.collect.Iterators; import com.google.common.collect.Sets; /** * Map adapter that inherits entries from a parent map. This map's entries * have priority over the parent's. Modifications affect this map only. * Modification through views or iterators is not supported. */ public class InheritingMap extends ForwardingMap { private final Map map, parent; public InheritingMap(Map parent) { this(new HashMap(), parent); } public InheritingMap(Map map, Map parent) { this.map = map; this.parent = parent; } @SafeVarargs public static Map chain(Map... maps) { return chain(Iterators.forArray(maps)); } public static Map chain(Iterable> maps) { return chain(maps.iterator()); } public static Map chain(Iterator> maps) { if(!maps.hasNext()) return Collections.emptyMap(); final Map head = maps.next(); if(!maps.hasNext()) return head; return new InheritingMap<>(head, chain(maps)); } @Override protected Map delegate() { return map; } @Override public int size() { return Sets.union(map.keySet(), parent.keySet()).size(); } @Override public boolean isEmpty() { return map.isEmpty() && parent.isEmpty(); } @Override public boolean containsKey(Object key) { return map.containsKey(key) || parent.containsKey(key); } @Override public boolean containsValue(Object value) { return map.containsValue(value) || parent.containsValue(value); } @Override public V get(Object key) { if(map.containsKey(key)) { return map.get(key); } else { return parent.get(key); } } @Override public Set keySet() { return Sets.union(map.keySet(), parent.keySet()); } @Override public Collection values() { return Collections2.transform(keySet(), new Function() { @Override public V apply(K key) { return get(key); } }); } @Override public Set> entrySet() { // Union of child's entrySet and paren't entrySet with child's keys filtered out return Sets.union( map.entrySet(), Sets.filter( parent.entrySet(), new Predicate>() { @Override public boolean apply(Entry entry) { return !map.containsKey(entry.getKey()); } } ) ); } }