package freeboogie.util; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.Map; import java.util.Set; /** * It implements the {@code Map} interface but also has * methods {@code push} and {@code pop}, the latter removing * all the bindings added since the last push; bindings * that existed before the push but were overriden after * will return to their previous state. The {@code Map} * intefrace is not exactly followed. The methods {@code keySet}, * {@code values}, and {@code entrySet} are just copies and changes * in them do not reflect in the map, or viceversa. * * See the {@code main} method for some examples. * * @param <K> the type of the key * @param <V> the type of the values * * @author rgrig * @author reviewed by TODO */ public class StackedHashMap<K, V> implements Map<K, V> { // there is always a first element in |data| private LinkedList<LinkedHashMap<K, V>> data; /** * Constructs a new {@code StackedHashMap}. */ public StackedHashMap() { clear(); } /** * Marks this place so that all the key-value bindings * afterwards can be removed quickly with one {@code pop()} * operation. */ public void push() { data.addFirst(new LinkedHashMap<K, V>()); } /** * Removes all the bindings that were added since the last * {@code push}. If there is no previous {@code push} then * it is equivalent to {@code clear}. */ public void pop() { if (data.size() > 1) data.removeFirst(); else clear(); } /* @see java.util.Map#clear() */ public void clear() { data = new LinkedList<LinkedHashMap<K, V>>(); data.addFirst(new LinkedHashMap<K, V>()); } /* @see java.util.Map#containsKey(java.lang.Object) */ public boolean containsKey(Object key) { for (LinkedHashMap<K, V> h : data) if (h.containsKey(key)) return true; return false; } /* @see java.util.Map#containsValue(java.lang.Object) */ public boolean containsValue(Object value) { for (LinkedHashMap<K, V> h : data) if (h.containsValue(value)) return true; return false; } /* @see java.util.Map#entrySet() */ public Set<java.util.Map.Entry<K, V>> entrySet() { HashSet<K> seen = new HashSet<K>(); Set<Map.Entry<K, V>> s = new HashSet<Map.Entry<K, V>>(); for (LinkedHashMap<K, V> h : data) { for (Map.Entry<K, V> e : h.entrySet()) if (!seen.contains(e.getKey())) { seen.add(e.getKey()); s.add(e); } } return s; } /* @see java.util.Map#get(java.lang.Object) */ public V get(Object key) { for (LinkedHashMap<K, V> h : data) { V v = h.get(key); if (v != null) return v; } return null; } /* @see java.util.Map#isEmpty() */ public boolean isEmpty() { return size() == 0; } /* @see java.util.Map#keySet() */ public Set<K> keySet() { Set<K> s = new HashSet<K>(); for (LinkedHashMap<K, V> h : data) s.addAll(h.keySet()); return s; } /* @see java.util.Map#put(java.lang.Object, java.lang.Object) */ public V put(K key, V value) { return data.getFirst().put(key, value); } /* @see java.util.Map#putAll(java.util.Map) */ public void putAll(Map<? extends K, ? extends V> map) { data.getFirst().putAll(map); } /* @see java.util.Map#remove(java.lang.Object) */ public V remove(Object key) { V r = null; for (LinkedHashMap<K, V> h : data) { V v = h.remove(key); if (r == null) r = v; } return r; } /* @see java.util.Map#size() */ public int size() { int sz = 0; for (LinkedHashMap<K, V> h : data) sz += h.size(); return sz; } /* @see java.util.Map#values() */ public Collection<V> values() { ArrayList<V> v = new ArrayList<V>(); for (LinkedHashMap<K, V> h : data) v.addAll(h.values()); return v; } /** * To be used for debug. * @param <K> the key type * @param <V> the value type * @param s the stacked hash map to print */ public static <K, V> void print(StackedHashMap<K, V> s) { System.out.println("--- begin map ---"); for (Map.Entry e : s.entrySet()) System.out.println("" + e.getKey() + " -> " + e.getValue()); System.out.println("--- end map ---"); } /** * @param args */ public static void main(String[] args) { StackedHashMap<Character, Integer> test = new StackedHashMap<Character, Integer>(); test.put('a', 0); test.put('b', 1); test.put('c', 2); test.push(); test.put('c', 3); test.put('d', 4); print(test); // a->0, b->1, c->3, d->4 test.remove('b'); print(test); // a->0, c->3, d->4 test.pop(); print(test); // a->0, c->2 test.pop(); print(test); // empty test.pop(); print(test); // still empty test.put('a', 5); print(test); // a->5 } }