package com.hubspot.jinjava.util; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; public class ScopeMap<K, V> implements Map<K, V> { private final Map<K, V> scope; private final ScopeMap<K, V> parent; public ScopeMap() { this(null); } public ScopeMap(ScopeMap<K, V> parent) { this.scope = new HashMap<K, V>(); this.parent = parent; } public ScopeMap(ScopeMap<K, V> parent, Map<K, V> scope) { this(parent); this.scope.putAll(scope); } public ScopeMap<K, V> getParent() { return parent; } public Map<K, V> getScope() { return scope; } @Override public int size() { return keySet().size(); } @Override public boolean isEmpty() { return size() == 0; } @Override public boolean containsKey(Object key) { return get(key) != null; } @Override public boolean containsValue(Object value) { if (scope.containsValue(value)) { return true; } if (parent != null) { return parent.containsValue(value); } return false; } public V get(Object key, V defVal) { V val = get(key); if (val != null) { return val; } return defVal; } @Override public V get(Object key) { V val = scope.get(key); if (val != null) { return val; } if (parent != null) { return parent.get(key); } return null; } @Override public V put(K key, V value) { return scope.put(key, value); } @Override public V remove(Object key) { return scope.remove(key); } @Override public void putAll(Map<? extends K, ? extends V> m) { scope.putAll(m); } @Override public void clear() { scope.clear(); } @Override public Set<K> keySet() { Set<K> keys = new HashSet<>(); if (parent != null) { keys.addAll(parent.keySet()); } keys.addAll(scope.keySet()); return keys; } @Override public Collection<V> values() { Set<java.util.Map.Entry<K, V>> entrySet = entrySet(); Collection<V> values = new ArrayList<>(entrySet.size()); for (Map.Entry<K, V> entry : entrySet) { values.add(entry.getValue()); } return values; } @Override @SuppressFBWarnings(justification = "using overridden get() to do scoped retrieve with parent fallback", value = "WMI_WRONG_MAP_ITERATOR") public Set<java.util.Map.Entry<K, V>> entrySet() { Set<java.util.Map.Entry<K, V>> entries = new HashSet<>(); for (K key : keySet()) { entries.add(new ScopeMapEntry<K, V>(key, get(key), this)); } return entries; } public static class ScopeMapEntry<K, V> implements Map.Entry<K, V> { private final Map<K, V> map; private final K key; private V value; public ScopeMapEntry(K key, V value, Map<K, V> map) { this.key = key; this.value = value; this.map = map; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public V setValue(V value) { V old = value; this.value = value; map.put(key, value); return old; } } }