package xapi.collect.proxy; import xapi.collect.X_Collect; import xapi.collect.api.CollectionOptions; import xapi.collect.api.HasValues; import xapi.collect.api.ObjectTo; import xapi.fu.In1Out1; import xapi.fu.In2Out1; import xapi.fu.Out2; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; public class MapOf <K, V> implements CollectionProxy<K,V>, Map<K,V>, HasValues<K,V>, ObjectTo<K,V> { private final Class<K> keyClass; private final Map<K,V> map; private final Class<V> valueClass; public <Key extends K, Value extends V> MapOf(final Map<K, V> map, final Class<Key> keyClass, final Class<Value> valueClass) { this.map = map; this.keyClass = (Class<K>) keyClass; this.valueClass = (Class<V>)valueClass; } @Override public void clear() { map.clear(); } @Override public ObjectTo<K,V> clone(final CollectionOptions options) { final ObjectTo<K,V> into = X_Collect.newMap(keyClass, valueClass, options); for (final Entry<K,V> entry : map.entrySet()) { // do not give access to our map's entry objects; // this method is clone(), which implies copy and not reference sharing into.put(entry.getKey(), entry.getValue()); } return into; } @Override public Class<?> componentType() { return valueType(); } @Override public boolean containsKey(final Object key) { return map.containsKey(key); } @Override public boolean containsValue(final Object value) { return map.containsValue(value); } @Override public Iterable<java.util.Map.Entry<K,V>> entries() { return entrySet(); } @Override @SuppressWarnings("unchecked") public Entry<K,V> entryFor(final Object key) { // Encourage inlining; EntryProxy won't get loaded if we only // use CollectionProxy based maps. if (map instanceof CollectionProxy) { return ((CollectionProxy<K,V>)map).entryFor(key); } // Rather than iterate to get the map's actual Entry, // We'll just return a proxy object that fulfills the same duty. class EntryProxy implements Entry<K, V> { @Override public K getKey() { return (K)key; } @Override public V getValue() { return map.get(key); } @Override public V setValue(final V value) { return map.put((K)key, value); } } return new EntryProxy(); } @Override public Set<java.util.Map.Entry<K,V>> entrySet() { return map.entrySet(); } @Override public V get(final Object key) { return map.get(key); } @Override public boolean isEmpty() { return map.isEmpty(); } @Override public Iterable<K> keys() { return keySet(); } @Override public Set<K> keySet() { return map.keySet(); } @Override public Class<K> keyType() { return keyClass; } @Override public V put(final Entry<K, V> item) { return map.put(item.getKey(), item.getValue()); } @Override public V put(final K key, final V value) { return map.put(key, value); } @Override public void putAll(final Iterable<java.util.Map.Entry<K,V>> items) { for (final Entry<K, V> entry : items) { map.put(entry.getKey(), entry.getValue()); } } @Override public void addAll(Iterable<Out2<K, V>> items) { items.forEach(item->map.put(item.out1(), item.out2())); } @Override public void putAll(final Map<? extends K,? extends V> m) { map.putAll(m); } @Override public V remove(final Object key) { return map.remove(key); } @Override public void removeAll(final Iterable<K> keys) { for (final K key : keys) { map.remove(key); } } @Override @SuppressWarnings("unchecked") public void setValue(final Object key, final Object value) { map.put((K)key, (V)value); } @Override public int size() { return map.size(); } @Override @SuppressWarnings("unchecked") public V[] toArray() { final V[] values = (V[]) Array.newInstance(valueClass, map.size()); map.values().toArray(values); return values; } @Override public Collection<V> toCollection(Collection<V> into) { if (into == null) { into = new ArrayList<V>(); } into.addAll(map.values()); return into; } @Override public Map<K,V> toMap(Map<K,V> into) { if (into == null) { into = new LinkedHashMap<K,V>(); } into.putAll(map); return into; } @Override public Collection<V> values() { return map.values(); } @Override public Class<V> valueType() { return valueClass; } @Override public V getOrCompute(K key, In1Out1<K, V> factory) { V existing = get(key); if (existing == null) { existing = factory.io(key); put(key, existing); } return existing; } @Override public boolean readWhileTrue(In2Out1<K, V, Boolean> callback) { for (Entry<K, V> entry : entries()) { if (!callback.io(entry.getKey(), entry.getValue())) { return false; } } return true; } }