#region Copyright & License Information /* * Copyright 2007-2015 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, * see COPYING. */ #endregion using System; using System.Collections; using Eluant; namespace OpenRA.Scripting { public static class LuaValueExts { public static Type WrappedClrType(this LuaValue value) { object inner; if (value.TryGetClrObject(out inner)) return inner.GetType(); return value.GetType(); } public static bool TryGetClrValue(this LuaValue value, out T clrObject) { object temp; var ret = value.TryGetClrValue(typeof(T), out temp); clrObject = ret ? (T)temp : default(T); return ret; } public static bool TryGetClrValue(this LuaValue value, Type t, out object clrObject) { object temp; // Is t a nullable? // If yes, get the underlying type var nullable = Nullable.GetUnderlyingType(t); if (nullable != null) t = nullable; // Value wraps a CLR object if (value.TryGetClrObject(out temp)) { if (temp.GetType() == t) { clrObject = temp; return true; } } if (value is LuaNil && !t.IsValueType) { clrObject = null; return true; } if (value is LuaBoolean && t.IsAssignableFrom(typeof(bool))) { clrObject = value.ToBoolean(); return true; } if (value is LuaNumber && t.IsAssignableFrom(typeof(double))) { clrObject = value.ToNumber().Value; return true; } // Need an explicit test for double -> int // TODO: Lua 5.3 will introduce an integer type, so this will be able to go away if (value is LuaNumber && t.IsAssignableFrom(typeof(int))) { clrObject = (int)value.ToNumber().Value; return true; } if (value is LuaString && t.IsAssignableFrom(typeof(string))) { clrObject = value.ToString(); return true; } if (value is LuaFunction && t.IsAssignableFrom(typeof(LuaFunction))) { clrObject = value; return true; } if (value is LuaTable && t.IsAssignableFrom(typeof(LuaTable))) { clrObject = value; return true; } // Translate LuaTable -> object[] if (value is LuaTable && t.IsArray) { var innerType = t.GetElementType(); var table = (LuaTable)value; var array = Array.CreateInstance(innerType, table.Count); var i = 0; foreach (var kv in table) { object element; if (innerType == typeof(LuaValue)) element = kv.Value; else if (!kv.Value.TryGetClrValue(innerType, out element)) throw new LuaException("Unable to convert table value of type {0} to type {1}".F(kv.Value.WrappedClrType(), innerType)); array.SetValue(element, i++); } clrObject = array; return true; } // Value isn't of the requested type. // Set a default output value and return false // Value types are assumed to specify a default constructor clrObject = t.IsValueType ? Activator.CreateInstance(t) : null; return false; } public static LuaValue ToLuaValue(this object obj, ScriptContext context) { if (obj is LuaValue) return (LuaValue)obj; if (obj == null) return LuaNil.Instance; if (obj is double) return (LuaValue)(double)obj; if (obj is int) return (LuaValue)(int)obj; if (obj is bool) return (LuaValue)(bool)obj; if (obj is string) return (LuaValue)(string)obj; if (obj is IScriptBindable) { // Object needs additional notification / context var notify = obj as IScriptNotifyBind; if (notify != null) notify.OnScriptBind(context); return new LuaCustomClrObject(obj); } if (obj is Array) { var array = obj as Array; var i = 1; var table = context.CreateTable(); foreach (var x in array) table.Add(i++, x.ToLuaValue(context)); return table; } throw new InvalidOperationException("Cannot convert type '{0}' to Lua. Class must implement IScriptBindable.".F(obj.GetType())); } } }