/src/sys/dotnet/fan/sys/Map.cs
C# | 701 lines | 569 code | 89 blank | 43 comment | 146 complexity | 4a16bf90e65b54398e9d5664244eed04 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
- //
- // Copyright (c) 2006, Brian Frank and Andy Frank
- // Licensed under the Academic Free License version 3.0
- //
- // History:
- // 28 Sep 06 Andy Frank Creation
- //
- using System;
- using System.Collections;
- using System.Diagnostics;
- using System.Collections.Specialized;
- using System.Text;
- using Fanx.Serial;
- namespace Fan.Sys
- {
- /// <summary>
- /// Map is a hashm_map of key value pairs.
- /// </summary>
- public sealed class Map : FanObj, Literal
- {
- //////////////////////////////////////////////////////////////////////////
- // Constructors
- //////////////////////////////////////////////////////////////////////////
- public static Map make(Type type)
- {
- return new Map((MapType)type, new Hashtable());
- }
- public Map(Type k, Type v) : this(new MapType(k, v), new Hashtable())
- {
- }
- public Map(MapType type) : this(type, new Hashtable())
- {
- }
- public Map(MapType type, IDictionary map)
- {
- if (type == null || map == null)
- {
- Console.WriteLine(new StackTrace(true));
- throw new NullErr().val;
- }
- this.m_type = type;
- this.m_map = map;
- }
- //////////////////////////////////////////////////////////////////////////
- // Identity
- //////////////////////////////////////////////////////////////////////////
- public override Type @typeof()
- {
- return m_type;
- }
- //////////////////////////////////////////////////////////////////////////
- // Methods
- //////////////////////////////////////////////////////////////////////////
- public bool isEmpty()
- {
- return m_map.Count == 0;
- }
- public long size()
- {
- return m_map.Count;
- }
- public object get(object key)
- {
- object val = m_map[key];
- if (val != null) return val;
- if (m_def == null) return null;
- return containsKey(key) ? null : m_def;
- }
- public object get(object key, object def)
- {
- object val = m_map[key];
- if (val != null) return val;
- if (def == null) return null;
- return containsKey(key) ? null : def;
- }
- public object getOrThrow(object key)
- {
- object val = m_map[key];
- if (val != null) return val;
- if (containsKey(key)) return null;
- throw UnknownKeyErr.make(key.ToString()).val;
- }
- public bool containsKey(object key)
- {
- return m_map.Contains(key);
- }
- public List keys()
- {
- return new List(m_type.m_k, m_map.Keys);
- }
- public List vals()
- {
- return new List(m_type.m_v, m_map.Values);
- }
- public Map set(object key, object val)
- {
- modify();
- if (key == null)
- throw NullErr.make("key is null").val;
- if (!isImmutable(key))
- throw NotImmutableErr.make("key is not immutable: " + @typeof(key)).val;
- m_map[key] = val;
- return this;
- }
- public Map add(object key, object val)
- {
- modify();
- if (key == null)
- throw NullErr.make("key is null").val;
- if (!isImmutable(key))
- throw NotImmutableErr.make("key is not immutable: " + @typeof(key)).val;
- if (containsKey(key))
- throw ArgErr.make("Key already mapped: " + key).val;
- m_map[key] = val;
- return this;
- }
- public object getOrAdd(object key, Func valFunc)
- {
- if (containsKey(key)) return get(key);
- object val = valFunc.call(key);
- add(key, val);
- return val;
- }
- public Map setAll(Map m)
- {
- modify();
- IDictionaryEnumerator en = m.m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- m_map[key] = val;
- }
- return this;
- }
- public Map addAll(Map m)
- {
- modify();
- IDictionaryEnumerator en = m.m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- add(key, val);
- }
- return this;
- }
- public Map setList(List list) { return setList(list, null); }
- public Map setList(List list, Func f)
- {
- modify();
- if (f == null)
- {
- for (int i=0; i<list.sz(); ++i)
- set(list.get(i), list.get(i));
- }
- else if (f.@params().sz() == 1)
- {
- for (int i=0; i<list.sz(); ++i)
- set(f.call(list.get(i)), list.get(i));
- }
- else
- {
- for (int i=0; i<list.sz(); ++i)
- set(f.call(list.get(i), i), list.get(i));
- }
- return this;
- }
- public Map addList(List list) { return addList(list, null); }
- public Map addList(List list, Func f)
- {
- modify();
- if (f == null)
- {
- for (int i=0; i<list.sz(); ++i)
- add(list.get(i), list.get(i));
- }
- else if (f.@params().sz() == 1)
- {
- for (int i=0; i<list.sz(); ++i)
- add(f.call(list.get(i)), list.get(i));
- }
- else
- {
- for (int i=0; i<list.sz(); ++i)
- add(f.call(list.get(i), i), list.get(i));
- }
- return this;
- }
- public object remove(object key)
- {
- modify();
- object val = m_map[key];
- m_map.Remove(key);
- return val;
- }
- public Map dup()
- {
- Map dup = new Map(m_type);
- dup.m_map = cloneMap(this.m_map);
- return dup;
- }
- public Map clear()
- {
- modify();
- m_map.Clear();
- return this;
- }
- public bool caseInsensitive() { return m_caseInsensitive; }
- public void caseInsensitive(bool v)
- {
- modify();
- if (m_type.m_k != Sys.StrType)
- throw UnsupportedErr.make("Map not keyed by string: " + m_type).val;
- if (m_map.Count != 0)
- throw UnsupportedErr.make("Map not empty").val;
- if (v && ordered())
- throw UnsupportedErr.make("Map cannot be caseInsensitive and ordered").val;
- if (this.m_caseInsensitive == v) return;
- this.m_caseInsensitive = v;
- if (m_caseInsensitive)
- m_map = new Hashtable(new CIEqualityComparer());
- else
- m_map = new Hashtable();
- }
- public bool ordered()
- {
- return m_map is OrderedDictionary;
- }
- public void ordered(bool v)
- {
- modify();
- if (m_map.Count != 0)
- throw UnsupportedErr.make("Map not empty").val;
- if (v && caseInsensitive())
- throw UnsupportedErr.make("Map cannot be caseInsensitive and ordered").val;
- if (ordered() == v) return;
- if (v)
- m_map = new OrderedDictionary();
- else
- m_map = new Hashtable();
- }
- public object def() { return m_def; }
- public void def(object v)
- {
- modify();
- if (v != null && !isImmutable(v))
- throw NotImmutableErr.make("def must be immutable: " + @typeof(v)).val;
- this.m_def = v;
- }
- public override bool Equals(object that)
- {
- if (that is Map)
- {
- if (!m_type.Equals(@typeof(that)))
- return false;
- IDictionary thatMap = ((Map)that).m_map;
- if (m_map.Count != thatMap.Count)
- return false;
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- object test = thatMap[key];
- if (val == null)
- {
- if (test != null) return false;
- }
- else if (!val.Equals(test))
- {
- return false;
- }
- }
- return true;
- }
- return false;
- }
- public override int GetHashCode() { return (int)hash(); }
- public override long hash()
- {
- int hash = 0;
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- hash += key.GetHashCode() ^ (val == null ? 0 : val.GetHashCode());
- }
- return hash;
- }
- public override string toStr()
- {
- if (m_map.Count == 0) return "[:]";
- StringBuilder s = new StringBuilder(32+m_map.Count*32);
- s.Append("[");
- bool first = true;
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- if (!first) s.Append(", ");
- else first = false;
- s.Append(key).Append(':').Append(val);
- }
- s.Append("]");
- return s.ToString();
- }
- public void encode(ObjEncoder @out)
- {
- // route back to obj encoder
- @out.writeMap(this);
- }
- //////////////////////////////////////////////////////////////////////////
- // Iterators
- //////////////////////////////////////////////////////////////////////////
- public void each(Func f)
- {
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- f.call(val, key);
- }
- }
- public object eachWhile(Func f)
- {
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- object r = f.call(val, key);
- if (r != null) return r;
- }
- return null;
- }
- public object find(Func f)
- {
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- if (f.call(val, key) == Boolean.True)
- return val;
- }
- return null;
- }
- public Map findAll(Func f)
- {
- Map acc = new Map(m_type);
- if (this.ordered()) acc.ordered(true);
- if (this.caseInsensitive()) acc.caseInsensitive(true);
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- if (f.call(val, key) == Boolean.True)
- acc.set(key, val);
- }
- return acc;
- }
- public Map exclude(Func f)
- {
- Map acc = new Map(m_type);
- if (this.ordered()) acc.ordered(true);
- if (this.caseInsensitive()) acc.caseInsensitive(true);
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- if (f.call(val, key) == Boolean.False)
- acc.set(key, val);
- }
- return acc;
- }
- public bool any(Func f)
- {
- if (m_map.Count == 0) return false;
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- if (f.call(val, key) == Boolean.True)
- return true;
- }
- return false;
- }
- public bool all(Func f)
- {
- if (m_map.Count == 0) return true;
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- if (f.call(val, key) == Boolean.False)
- return false;
- }
- return true;
- }
- public object reduce(object reduction, Func f)
- {
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- reduction = f.call(reduction, val, key);
- }
- return reduction;
- }
- public Map map(Func f)
- {
- Type r = f.returns();
- if (r == Sys.VoidType) r = Sys.ObjType.toNullable();
- Map acc = new Map(m_type.m_k, r);
- if (this.ordered()) acc.ordered(true);
- if (this.caseInsensitive()) acc.caseInsensitive(true);
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- acc.set(key, f.call(val, key));
- }
- return acc;
- }
- //////////////////////////////////////////////////////////////////////////
- // Join
- //////////////////////////////////////////////////////////////////////////
- public string join(string sep) { return join(sep, null); }
- public string join(string sep, Func f)
- {
- int size = (int)this.size();
- if (size == 0) return "";
- StringBuilder s = new StringBuilder(32+size*32);
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- if (s.Length > 0) s.Append(sep);
- if (f == null)
- s.Append(key).Append(": ").Append(val);
- else
- s.Append(f.call(val, key));
- }
- return s.ToString();
- }
- public string toCode()
- {
- int size = (int)this.size();
- StringBuilder s = new StringBuilder(32+size*32);
- s.Append(@typeof().signature());
- s.Append('[');
- if (size == 0) s.Append(':');
- bool first = true;
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- if (first) first = false;
- else s.Append(',').Append(' ');
- s.Append(FanObj.trap(key, "toCode", null))
- .Append(':')
- .Append(FanObj.trap(val, "toCode", null));
- }
- s.Append(']');
- return s.ToString();
- }
- //////////////////////////////////////////////////////////////////////////
- // Readonly
- //////////////////////////////////////////////////////////////////////////
- public bool isRW()
- {
- return !m_isReadonly;
- }
- public bool isRO()
- {
- return m_isReadonly;
- }
- public Map rw()
- {
- if (!m_isReadonly) return this;
- Map rw = new Map(m_type);
- rw.m_map = cloneMap(m_map);
- rw.m_isReadonly = false;
- rw.m_readonlyMap = this;
- rw.m_caseInsensitive = m_caseInsensitive;
- rw.m_def = m_def;
- return rw;
- }
- public Map ro()
- {
- if (m_isReadonly) return this;
- if (m_readonlyMap == null)
- {
- Map ro = new Map(m_type);
- ro.m_map = m_map;
- ro.m_isReadonly = true;
- ro.m_caseInsensitive = m_caseInsensitive;
- ro.m_def = m_def;
- m_readonlyMap = ro;
- }
- return m_readonlyMap;
- }
- public override bool isImmutable()
- {
- return m_immutable;
- }
- public override object toImmutable()
- {
- if (m_immutable) return this;
- // make safe copy
- IDictionary temp;
- if (caseInsensitive()) temp = new Hashtable(new CIEqualityComparer());
- else if (ordered()) temp = new OrderedDictionary();
- else temp = new Hashtable();
- IDictionaryEnumerator en = m_map.GetEnumerator();
- while (en.MoveNext())
- {
- object key = en.Key;
- object val = en.Value;
- if (val != null)
- {
- if (val is List)
- val = ((List)val).toImmutable();
- else if (val is Map)
- val = ((Map)val).toImmutable();
- else if (!isImmutable(val))
- throw NotImmutableErr.make("Item [" + key + "] not immutable " + @typeof(val)).val;
- }
- temp[key] = val;
- }
- // return new m_immutable m_map
- Map ro = new Map(m_type, temp);
- ro.m_isReadonly = true;
- ro.m_immutable = true;
- ro.m_caseInsensitive = m_caseInsensitive;
- ro.m_def = m_def;
- return ro;
- }
- private void modify()
- {
- // if readonly then throw readonly exception
- if (m_isReadonly)
- throw ReadonlyErr.make("Map is readonly").val;
- // if we have a cached m_readonlyMap, then detach
- // it so it remains m_immutable
- if (m_readonlyMap != null)
- {
- m_readonlyMap.m_map = cloneMap(m_map);
- m_readonlyMap = null;
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // C#
- //////////////////////////////////////////////////////////////////////////
- public IDictionaryEnumerator pairsIterator()
- {
- return m_map.GetEnumerator();
- }
- public IEnumerator keysEnumerator()
- {
- return m_map.Keys.GetEnumerator();
- }
- internal IDictionary cloneMap(IDictionary dict)
- {
- if (dict is Hashtable) return (IDictionary)((Hashtable)dict).Clone();
- if (dict is OrderedDictionary)
- {
- OrderedDictionary dup = new OrderedDictionary();
- IDictionaryEnumerator en = dict.GetEnumerator();
- while (en.MoveNext()) dup[en.Key] = en.Value;
- return dup;
- }
- throw new Exception(dict.ToString());
- }
- //////////////////////////////////////////////////////////////////////////
- // CIEqualityComparer (Case Insensitive)
- //////////////////////////////////////////////////////////////////////////
- class CIEqualityComparer : IEqualityComparer
- {
- public new bool Equals(object x, object y)
- {
- return FanStr.equalsIgnoreCase((string)x, (string)y);
- }
- public int GetHashCode(object obj)
- {
- return FanStr.caseInsensitiveHash((string)obj);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // Fields
- //////////////////////////////////////////////////////////////////////////
- private MapType m_type;
- private IDictionary m_map;
- private Map m_readonlyMap;
- private bool m_isReadonly;
- private bool m_immutable;
- private bool m_caseInsensitive = false;
- private object m_def;
- }
- }