
// // Copyright (c) 2006, Brian Frank and Andy Frank // Licensed under the Academic Free License version 3.0 // // History: // 30 Jan 06 Brian Frank Creation // ** ** Map is a hash map of key/value pairs. ** ** See `docCookbook::Maps` for coding examples. ** final class Map { ////////////////////////////////////////////////////////////////////////// // Constructor ////////////////////////////////////////////////////////////////////////// ** ** Constructor with of type (must be Map type). ** new make(Type type) ////////////////////////////////////////////////////////////////////////// // Identity ////////////////////////////////////////////////////////////////////////// ** ** Two Maps are equal if they have the same number of equal key/value pairs. ** override Bool equals(Obj that) ** ** Return platform dependent hashcode based on hash of the keys and values. ** override Int hash() ////////////////////////////////////////////////////////////////////////// // Methods ////////////////////////////////////////////////////////////////////////// ** ** Return if size() == 0. This method is idempotent. ** Bool isEmpty() ** ** Get the number of key/value pairs in the list. This ** method is idempotent. ** Int size() ** ** Get the value for the specified key. If key is not ** mapped, then return the value of def (null by default). ** This method is idempotent. Shortcut is a[key]. ** V get(K key, V def := null) ** ** Return if the specified key is mapped. ** This method is idempotent. ** Bool containsKey(K key) ** ** Get a list of all the mapped keys. This method is idempotent. ** K[] keys() ** ** Get a list of all the mapped values. This method is idempotent. ** V[] values() ** ** Create a shallow duplicate copy of this Map. The keys and ** values themselves are not duplicated. This method is idempotent. ** M dup() ** ** Set the value for the specified key. If the key is already ** mapped, this overwrites the old value. If key is not yet mapped ** this adds the key/value pair to the map. Return this. If key ** does not return true for Obj.isImmutable, then throw NotImmutableErr. ** If key is null throw NullErr. Throw ReadonlyErr if readonly. ** M set(K key, V val) ** ** Add the specified key/value pair to the map. If the key is ** already mapped, then throw the ArgErr. Return this. If key ** does not return true for Obj.isImmutable, then throw NotImmutableErr. ** If key is null throw NullErr. Throw ReadonlyErr if readonly. ** M add(K key, V val) ** ** Append the specified map to this map by setting every key/value in ** m in this map. Keys in m not yet mapped are added and keys already ** mapped are overwritten. Return this. Throw ReadonlyErr if readonly. ** This method is semanatically equivalent to: ** m.each |K k, V v| { this.set(k, v) } ** M setAll(M m) ** ** Append the specified map to this map by adding every key/value in ** m in this map. If any key in m is already mapped then this method ** will fail (any previous keys will remain mapped potentially leaving ** this map in an inconsistent state). Return this. Throw ReadonlyErr if ** readonly. This method is semanatically equivalent to: ** m.each |K k, V v| { this.add(k, v) } ** M addAll(M m) ** ** Remove the key/value pair identified by the specified key ** from the map and return the value. If the key was not mapped ** then return null. Throw ReadonlyErr if readonly. ** V remove(K key) ** ** Remove all key/value pairs from the map. Return this. ** Throw ReadonlyErr if readonly. ** Void clear() ** ** This field configures case sensitivity for maps with Str keys. When ** set to true, Str keys are compared without regard to case for the following ** methods: get, containsKey, set, add, setAll, addAll, and remove methods. ** Only ASCII character case is taken into account. The original case ** is preserved (keys aren't made all lower or upper case). This field ** defaults to false. ** ** Getting this field is idempotent. If you attempt to set this method ** on a map which is not empty or not typed to use Str keys, then throw ** UnsupportedOperation. Throw ReadonlyErr if set when readonly. ** Bool caseInsensitive := false ////////////////////////////////////////////////////////////////////////// // Conversion ////////////////////////////////////////////////////////////////////////// ** ** Return a string representation the Map. This method is idempotent. ** override Str toStr() ////////////////////////////////////////////////////////////////////////// // Iterators ////////////////////////////////////////////////////////////////////////// ** ** Call the specified function for every key/value in the list. ** This method is idempotent. ** Void each(|V value, K key| c) ** ** Return the first value in the map for which c returns true. ** If c returns false for every pair, then return null. This ** method is idempotent. ** V find(|V value, K key->Bool| c) ** ** Return a new map containing the key/value pairs for which c ** returns true. If c returns false for every item, then return ** an empty map. The inverse of this method is exclude(). This ** method is idempotent. ** M findAll(|V value, K key->Bool| c) ** ** Return a new map containing the key/value pairs for which c ** returns false. If c returns true for every item, then return ** an empty list. The inverse of this method is findAll(). This ** method is idempotent. ** ** Example: ** map := ["off":0, "slow":50, "fast":100] ** map.exclude |Int v->Bool| { return v == 0 } => ["slow":50, "fast":100] ** M exclude(|V item, K key->Bool| c) ** ** Reduce is used to iterate through every value in the map ** to reduce the map into a single value called the reduction. ** The initial value of the reduction is passed in as the init ** parameter, then passed back to the closure along with each ** item. This method is idempotent. ** ** Example: ** m := ["2":2, "3":3, "4":4] ** m.reduce(100) |Obj r, Int v->Obj| { return (Int)r + v } => 109 ** Obj reduce(Obj init, |Obj reduction, V item, K key->Obj| c) ** ** Create a new map with the same keys, but apply the specified ** closure to generate new values. This method is idempotent. ** ** Example: ** m := [2:2, 3:3, 4:4] ** x := m.map(Str:Int[:]) |Int v->Obj| { return v*2 } ** x => [2:4, 3:6, 4:8] ** M map(Map acc, |V item, K key->Obj| c) ////////////////////////////////////////////////////////////////////////// // Readonly ////////////////////////////////////////////////////////////////////////// ** ** Return if this Map is readonly. A readonly Map is guaranteed ** to be immutable (although its values may be mutable themselves). ** Any attempt to modify a readonly Map will result in ReadonlyErr. ** Use rw() to get a read-write Map from a readonly Map. Methods ** documented as idempotent may be used safely with a readonly Map. ** This method is idempotent. ** Bool isRO() ** ** Return if this Map is read-write. A read-write Map is mutable ** and may be modified. Use ro() to get a readonly Map from a ** read-write Map. This method is idempotent. ** Bool isRW() ** ** Get a readonly, immutable Map instance with the same contents ** as this Map (although its values may be mutable themselves). ** If this Map is already readonly, then return this. Only methods ** documented as idempotent may be used safely with a readonly ** Map, all others will throw ReadonlyErr. This method is ** idempotent. ** M ro() ** ** Get a read-write, mutable Map instance with the same contents ** as this Map. If this Map is already read-write, then return this. ** This method is idempotent. ** M rw() ** ** Return an immutable Map which returns true for Obj.isImmtable. ** If this Map is already immutable, then return this. This method ** is effectively a "deep ro()" which guarantees that if any values ** are Lists or Maps, then they are made immutable by recursively calling ** toImmutable. All other values must return true for Obj.isImmutable, ** otherwise NotImmutableErr is thrown. This method must be used ** whenever setting a const Map field. This method is idempotent. ** M toImmutable() }