using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Language.Lua { public class LuaTable : LuaValue { private List list; private Dictionary dict; //[PixelCrushers] Cache key values for faster lookup: private Dictionary m_intKeyCache = new Dictionary(); private Dictionary m_stringKeyCache = new Dictionary(); public LuaTable() { } public LuaTable(LuaTable parent) { this.MetaTable = new LuaTable(); this.MetaTable.SetNameValue("__index", parent); this.MetaTable.SetNameValue("__newindex", parent); } public LuaTable MetaTable { get; set; } public override object Value { get { return this; } } public override string GetTypeCode() { return "table"; } public int Length { get { if (this.list == null) { return 0; } else { return this.list.Count; } } } public int Count { get { if (this.dict == null) { return 0; } else { return this.dict.Count; } } } public override string ToString() { if (this.MetaTable != null) { LuaFunction function = this.MetaTable.GetValue("__tostring") as LuaFunction; if (function != null) { return function.Invoke(new LuaValue[] { this }).ToString(); } } return "Table " + this.GetHashCode(); } public List List //[PixelCrushers] { get { if (this.list == null) this.list = new List(); return this.list; } } public Dictionary Dict //[PixelCrushers] { get { if (this.dict == null) this.dict = new Dictionary(); return this.dict; } } public void AddRaw(string key, LuaValue value) //[PixelCrushers] { if (m_stringKeyCache.ContainsKey(key)) { var keyValue = m_stringKeyCache[key]; Dict[keyValue] = value; } else { var keyValue = new LuaString(key); Dict[keyValue] = value; m_stringKeyCache[key] = keyValue; } } public void AddRaw(int key, LuaValue value) //[PixelCrushers] { if (m_intKeyCache.ContainsKey(key)) { var keyValue = m_intKeyCache[key]; Dict[keyValue] = value; } else if (key == this.Length + 1) { this.AddValue(value); } else if (key > 0 && key <= this.Length) { this.list[key - 1] = value; } else { var keyValue = new LuaNumber(key); Dict[keyValue] = value; m_intKeyCache[key] = keyValue; } } public IEnumerable ListValues { get { return this.list; } } public IEnumerable Keys { get { if (this.Length > 0) { for (int index = 1; index <= this.list.Count; index++) { yield return new LuaNumber(index); } } if (this.Count > 0) { foreach (LuaValue key in this.dict.Keys) { yield return key; } } } } public IEnumerable> KeyValuePairs { get { return this.dict; } } public bool ContainsKey(LuaValue key) { if (this.dict != null) { if (this.dict.ContainsKey(key)) { return true; } } if (this.list != null) { LuaNumber index = key as LuaNumber; if (index != null && index.Number == (int)index.Number) { return index.Number >= 1 && index.Number <= this.list.Count; } } return false; } public void AddValue(LuaValue value) { if (this.list == null) { this.list = new List(); } this.list.Add(value); } public void InsertValue(int index, LuaValue value) { if (index > 0 && index <= this.Length + 1) { this.list.Insert(index - 1, value); } else { throw new ArgumentOutOfRangeException("index"); } } public bool Remove(LuaValue item) { return this.list.Remove(item); } public void RemoveAt(int index) { this.list.RemoveAt(index - 1); } public void Sort() { this.list.Sort((a, b) => { LuaNumber n = a as LuaNumber; LuaNumber m = b as LuaNumber; if (n != null && m != null) { return n.Number.CompareTo(m.Number); } LuaString s = a as LuaString; LuaString t = b as LuaString; if (s != null && t != null) { return s.Text.CompareTo(t.Text); } return 0; }); } public void Sort(LuaFunction compare) { this.list.Sort((a, b) => { LuaValue result = compare.Invoke(new LuaValue[] { a, b }); LuaBoolean boolValue = result as LuaBoolean; if (boolValue != null && boolValue.BoolValue == true) { return 1; } else { return -1; } }); } public LuaValue GetValue(int index) { if (index > 0 && index <= this.Length) { return this.list[index - 1]; } //[PixelCrushers] if (dict != null) { if (m_intKeyCache.ContainsKey(index) && dict.ContainsKey(m_intKeyCache[index])) return dict[m_intKeyCache[index]]; return GetValue(index.ToString()); } return LuaNil.Nil; } public LuaValue GetValue(string name) { LuaValue key = this.GetKey(name); if (key == LuaNil.Nil) { if (this.MetaTable != null) { return this.GetValueFromMetaTable(name); } return LuaNil.Nil; } else { return this.dict[key]; } } public LuaValue GetKey(string key) { if (this.dict == null) return LuaNil.Nil; if (m_stringKeyCache.ContainsKey(key)) return m_stringKeyCache[key]; foreach (LuaValue value in this.dict.Keys) { //[PixelCrushers] //LuaString str = value as LuaString; //if (str != null && string.Equals(str.Text, key, StringComparison.Ordinal)) //{ // return value; //} //LuaString str = value as LuaString; if (value != null && string.Equals(value.ToString(), key, StringComparison.Ordinal)) { return value; } } return LuaNil.Nil; } public void SetNameValue(string name, LuaValue value) { if (value == LuaNil.Nil) { this.RemoveKey(name); } else { this.RawSetValue(name, value); } } private void RemoveKey(string name) { LuaValue key = this.GetKey(name); if (key != LuaNil.Nil) { this.dict.Remove(key); } m_stringKeyCache.Remove(name); } public void SetKeyValue(LuaValue key, LuaValue value) { LuaNumber number = key as LuaNumber; if (number != null && number.Number == (int)number.Number) { int index = (int)number.Number; if (m_intKeyCache.ContainsKey(index) && Dict.ContainsKey(m_intKeyCache[index])) //[PixelCrushers] { Dict[m_intKeyCache[index]] = value; return; } if (index == this.Length + 1) { this.AddValue(value); return; } if (index > 0 && index <= this.Length) { this.list[index - 1] = value; return; } } if (value == LuaNil.Nil) { this.RemoveKey(key); return; } if (this.dict == null) { this.dict = new Dictionary(); } this.dict[key] = value; //[PixelCrushers] Update caches: int intValue; if (GetIntValue(key, out intValue)) { m_intKeyCache[intValue] = key; } else if (key is LuaString) { m_stringKeyCache[(key as LuaString).Text] = key; } } private bool GetIntValue(LuaValue value, out int intValue) { var number = value as LuaNumber; if (number != null && number.Number == (int)number.Number) { intValue = (int)number.Number; return true; } intValue = 0; return false; } private void RemoveKey(LuaValue key) { if (key != LuaNil.Nil && this.dict != null && this.dict.ContainsKey(key)) { this.dict.Remove(key); } //[PixelCrushers] Update caches: int intValue; if (GetIntValue(key, out intValue)) { m_intKeyCache.Remove(intValue); } else if (key is LuaString) { m_stringKeyCache.Remove((key as LuaString).Text); } } public LuaValue GetValue(LuaValue key) { if (key == LuaNil.Nil) { return LuaNil.Nil; } else { LuaNumber number = key as LuaNumber; if (number != null && number.Number == (int)number.Number) { int index = (int)number.Number; if (index > 0 && index <= this.Length) { return this.list[index - 1]; } } if (this.dict != null && this.dict.ContainsKey(key)) { return this.dict[key]; } if (this.MetaTable != null) { return this.GetValueFromMetaTable(key); } return LuaNil.Nil; } } private LuaValue GetValueFromMetaTable(string name) { LuaValue indexer = this.MetaTable.GetValue("__index"); LuaTable table = indexer as LuaTable; if (table != null) { return table.GetValue(name); } LuaFunction function = indexer as LuaFunction; if (function != null) { return function.Function.Invoke(new LuaValue[] { new LuaString(name) }); } return LuaNil.Nil; } private LuaValue GetValueFromMetaTable(LuaValue key) { LuaValue indexer = this.MetaTable.GetValue("__index"); LuaTable table = indexer as LuaTable; if (table != null) { return table.GetValue(key); } LuaFunction function = indexer as LuaFunction; if (function != null) { return function.Function.Invoke(new LuaValue[] { key }); } return LuaNil.Nil; } public LuaFunction Register(string name, LuaFunc function) { LuaFunction luaFunc = new LuaFunction(function); this.SetNameValue(name, luaFunc); return luaFunc; } /// /// [PixelCrushers] Registers a C# method by its MethodInfo. /// /// The Lua function instance wrapping the method. /// Name of the function in Lua. /// Target object containing the method (or null). /// Method info. public LuaFunction RegisterMethodFunction(string name, object target, System.Reflection.MethodInfo methodInfo) { LuaMethodFunction luaFunc = new LuaMethodFunction(target, methodInfo); this.SetNameValue(name, luaFunc); return luaFunc; } public LuaValue RawGetValue(LuaValue key) { if (this.dict != null && this.dict.ContainsKey(key)) { return this.dict[key]; } return LuaNil.Nil; } public void RawSetValue(string name, LuaValue value) { LuaValue key = this.GetKey(name); if (key == LuaNil.Nil) { key = new LuaString(name); m_stringKeyCache[name] = key; //[PixelCrushers] } if (this.dict == null) { this.dict = new Dictionary(); } this.dict[key] = value; } } }