package ns.foundation; import java.io.Serializable; import java.util.AbstractMap; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * Fully implemented except for NSKeyValueCodingAdditions */ public class NSDictionary<K, V> extends AbstractMap<K, V> implements Map<K, V>, NSKeyValueCoding, NSKeyValueCodingAdditions, _NSFoundationCollection, Cloneable, Serializable { private static final long serialVersionUID = -179272320896439111L; @SuppressWarnings("rawtypes") public static final NSDictionary EmptyDictionary = new NSDictionary(); public static final boolean CheckForNull = true; public static final boolean IgnoreNull = true; protected static final String NULL_NOT_ALLOWED = "Attempt to insert null into an NSDictionary."; protected Set<Map.Entry<K, V>> _entrySetCache; private Map<K, V> _backingStore; public NSDictionary() { _initializeWithCapacity(0); } protected NSDictionary(int capacity) { _initializeWithCapacity(capacity); } public NSDictionary(Map<K, V> map) { _initializeWithMap(map, NullHandling.CheckAndFail); } public NSDictionary(Map<K, V> map, boolean ignoreNull) { _initializeWithMap(map, ignoreNull ? NullHandling.NoCheck : NullHandling.CheckAndFail); } @SuppressWarnings("unchecked") public NSDictionary(NSArray<V> objects, NSArray<K> keys) { this((V[]) (objects != null ? objects.toArray() : null), (K[]) (keys != null ? keys.toArray() : null)); } public NSDictionary(NSDictionary<K, V> otherDictionary) { this((Map<K, V>) otherDictionary); } public NSDictionary(V[] objects, K[] keys) { _initFromKeyValues(objects, keys, NullHandling.CheckAndFail); } public NSDictionary(V object, K key) { if (key == null || object == null) throw new IllegalArgumentException("Object or key may not be null"); _initializeWithCapacity(1).put(key, object); } protected Map<K, V> _initializeWithCapacity(int capacity) { Map<K, V> map = new HashMap<K, V>(capacity); _setMap(Collections.unmodifiableMap(map)); return map; } protected void _initializeWithMap(Map<K, V> map, NullHandling nullHandling) { Map<K, V> store = _initializeWithCapacity(map.size()); if (map instanceof NSDictionary<?, ?> || nullHandling == NullHandling.NoCheck) store.putAll(map); else { for (Map.Entry<K, V> entry : map.entrySet()) { if (entry.getKey() == null || entry.getValue() == null) { if (nullHandling == NullHandling.CheckAndFail) throw new IllegalArgumentException("Key or value may not be null"); continue; } store.put(entry.getKey(), entry.getValue()); } } } protected void _initFromKeyValues(V[] objects, K[] keys, NullHandling nullHandling) { if (objects == null && keys == null) { _initializeWithCapacity(0); return; } if (keys == null || objects == null) throw new IllegalArgumentException("Both objects and keys cannot be null"); if (objects.length != keys.length) { throw new IllegalArgumentException("Attempt to create an " + getClass().getName() + " with a different number of objects and keys."); } Map<K, V> store = _initializeWithCapacity(objects.length); if (nullHandling != NullHandling.NoCheck) { for (int i = 0; i < objects.length; i++) { if (objects[i] == null || keys[i] == null) { if (nullHandling == NullHandling.CheckAndFail) throw new IllegalArgumentException("Attempt to insert a null into an " + getClass().getName() + "."); continue; } store.put(keys[i], objects[i]); } } else { for (int i = 0; i < objects.length; i++) { store.put(keys[i], objects[i]); } } } protected static <K, V, T extends NSDictionary<K, V>> T _initializeDictionaryWithMap(T dict, Map<K, V> map, NullHandling nullHandling) { if (map == null) throw new IllegalArgumentException("map may not be null"); if (!(map instanceof NSDictionary<?, ?>) && nullHandling != NullHandling.NoCheck && map.size() > 0) { try { if (map.containsValue(null) || map.containsKey(null)) { if (nullHandling == NullHandling.CheckAndFail) throw new IllegalArgumentException(NULL_NOT_ALLOWED); dict._initializeWithMap(map, nullHandling); return dict; } } catch (NullPointerException e) { // Must not support nulls either } } dict._setMap(map); return dict; } protected Map<K, V> mapNoCopy() { return _backingStore; } protected Object[] keysNoCopy() { return mapNoCopy().keySet().toArray(); } protected Object[] objectsNoCopy() { return mapNoCopy().values().toArray(); } protected Map<K, V> _setMap(Map<K, V> map) { return _backingStore = map; } public static <K, V> NSDictionary<K, V> asDictionary(Map<K, V> map) { return asDictionary(map, NullHandling.CheckAndFail); } public static <K, V> NSDictionary<K, V> asDictionary(Map<K, V> map, NullHandling nullHandling) { if (map == null || map.size() == 0) return emptyDictionary(); if (map.getClass() == NSDictionary.class) return (NSDictionary<K, V>) map; return _initializeDictionaryWithMap(new NSDictionary<K, V>(), Collections.unmodifiableMap(map), nullHandling); } public static <K, V> NSMutableDictionary<K, V> asMutableDictionary(Map<K, V> map) { return asMutableDictionary(map, NullHandling.CheckAndFail); } public static <K, V> NSMutableDictionary<K, V> asMutableDictionary(Map<K, V> map, NullHandling nullHandling) { if (map == null || map.size() == 0) return new NSMutableDictionary<K, V>(); if (map instanceof NSMutableDictionary<?, ?>) return (NSMutableDictionary<K, V>) map; return _initializeDictionaryWithMap(new NSMutableDictionary<K, V>(), map, nullHandling); } public NSArray<K> allKeys() { return new NSArray<K>(keySet()); } public NSArray<K> allKeysForObject(Object object) { if (object == null) return NSArray.emptyArray(); NSMutableArray<K> result = new NSMutableArray<K>(); for (Map.Entry<K, V> entry : entrySet()) { if (object.equals(entry.getValue())) { result.add(entry.getKey()); } } return result; } public NSArray<V> allValues() { return new NSArray<V>(values()); } public int count() { return mapNoCopy().size(); } @SuppressWarnings("unchecked") public static <K, V> NSDictionary<K, V> emptyDictionary() { return EmptyDictionary; } public HashMap<K, V> hashMap() { return new HashMap<K, V>(mapNoCopy()); } @Override public boolean equals(Object obj) { if (obj == this || obj == mapNoCopy()) return true; if (obj instanceof NSDictionary<?, ?> && mapNoCopy() == ((NSDictionary<?, ?>) obj).mapNoCopy()) return true; return super.equals(obj); } @Override public int hashCode() { return _shallowHashCode() ^ count(); } @Override public int _shallowHashCode() { return NSDictionary.class.hashCode(); } public boolean isEqualToDictionary(NSDictionary<?, ?> otherDictionary) { return equals(otherDictionary); } public NSDictionary<K, V> immutableClone() { return this; } public NSMutableDictionary<K, V> mutableClone() { return new NSMutableDictionary<K, V>(mapNoCopy()); } public Enumeration<K> keyEnumerator() { return allKeys().objectEnumerator(); } public Enumeration<V> objectEnumerator() { return allValues().objectEnumerator(); } public V objectForKey(Object key) { return mapNoCopy().get(key); } public NSArray<V> objectsForKeys(NSArray<K> keys, V notFoundMarker) { if (keys != null) { NSMutableArray<V> array = new NSMutableArray<V>(keys.size()); for (K key : keys) { V object = objectForKey(key); if (object != null) { array.addObject(object); continue; } if (notFoundMarker != null) { array.addObject(notFoundMarker); } } return array; } return NSArray.emptyArray(); } @Override public Object valueForKey(String key) { Object value = objectForKey(key); if ((value == null) && (key != null)) { if (key.equals("allValues")) return allValues(); if (key.equals("allKeys")) return allKeys(); if (key.equals("count")) return count(); } return value; } @Override public void takeValueForKey(Object value, String key) { throw new IllegalStateException(super.getClass().getName() + " is immutable."); } @Override public Object valueForKeyPath(String keyPath) { Object flattenedKeyPresent = objectForKey(keyPath); if (flattenedKeyPresent != null) return flattenedKeyPresent; return NSKeyValueCodingAdditions.DefaultImplementation.valueForKeyPath(this, keyPath); } @Override public void takeValueForKeyPath(Object value, String keyPath) { NSKeyValueCodingAdditions.DefaultImplementation.takeValueForKeyPath(this, value, keyPath); } private static final String UNSUPPORTED = " is not a supported operation in com.webobjects.foundation.NSDictionary"; @Override public void clear() { throw new UnsupportedOperationException("clear" + UNSUPPORTED); } @Override public NSDictionary<K, V> clone() { return this; } @Override public V remove(Object key) { throw new UnsupportedOperationException("remove" + UNSUPPORTED); } @Override public boolean containsKey(Object key) { return mapNoCopy().containsKey(key); } @Override public boolean containsValue(Object value) { return mapNoCopy().containsValue(value); } @Override public Set<Map.Entry<K, V>> entrySet() { if (_entrySetCache == null) { _entrySetCache = mapNoCopy().entrySet(); } return _entrySetCache; } @Override public V get(Object key) { return objectForKey(key); } @Override public boolean isEmpty() { return mapNoCopy().isEmpty(); } @Override public Set<K> keySet() { return mapNoCopy().keySet(); } @Override public int size() { return count(); } @Override public Collection<V> values() { return mapNoCopy().values(); } }