using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using MoonSharp.Interpreter.Compatibility; namespace MoonSharp.Interpreter.Interop { /// /// Utility class which may be used to set properties on an object of type T, from values contained in a Lua table. /// Properties must be decorated with the . /// This is a generic version of . /// /// The type of the object. public class PropertyTableAssigner : IPropertyTableAssigner { PropertyTableAssigner m_InternalAssigner; /// /// Initializes a new instance of the class. /// /// The expected missing properties, that is expected fields in the table with no corresponding property in the object. public PropertyTableAssigner(params string[] expectedMissingProperties) { m_InternalAssigner = new PropertyTableAssigner(typeof(T), expectedMissingProperties); } /// /// Adds an expected missing property, that is an expected field in the table with no corresponding property in the object. /// /// The name. public void AddExpectedMissingProperty(string name) { m_InternalAssigner.AddExpectedMissingProperty(name); } /// /// Assigns properties from tables to an object. /// /// The object. /// The table. /// Object is null /// A field does not correspond to any property and that property is not one of the expected missing ones. public void AssignObject(T obj, Table data) { m_InternalAssigner.AssignObject(obj, data); } /// /// Gets the type-unsafe assigner corresponding to this object. /// /// public PropertyTableAssigner GetTypeUnsafeAssigner() { return m_InternalAssigner; } /// /// Sets the subassigner for the given type. Pass null to remove usage of subassigner for the given type. /// /// Type of the property for which the subassigner will be used. /// The property assigner. public void SetSubassignerForType(Type propertyType, IPropertyTableAssigner assigner) { m_InternalAssigner.SetSubassignerForType(propertyType, assigner); } /// /// Sets the subassigner for the given type /// /// Type of the property for which the subassigner will be used. /// The property assigner. public void SetSubassigner(PropertyTableAssigner assigner) { m_InternalAssigner.SetSubassignerForType(typeof(SubassignerType), assigner); } /// /// Assigns the properties of the specified object without checking the type. /// /// The object. /// The data. void IPropertyTableAssigner.AssignObjectUnchecked(object o, Table data) { AssignObject((T)o, data); } } /// /// Utility class which may be used to set properties on an object from values contained in a Lua table. /// Properties must be decorated with the . /// See for a generic compile time type-safe version. /// public class PropertyTableAssigner : IPropertyTableAssigner { Type m_Type; Dictionary m_PropertyMap = new Dictionary(); Dictionary m_SubAssigners = new Dictionary(); /// /// Initializes a new instance of the class. /// /// The type of the object. /// The expected missing properties, that is expected fields in the table with no corresponding property in the object. /// /// Type cannot be a value type. /// public PropertyTableAssigner(Type type, params string[] expectedMissingProperties) { m_Type = type; if (Framework.Do.IsValueType(m_Type)) throw new ArgumentException("Type cannot be a value type."); foreach(string property in expectedMissingProperties) { m_PropertyMap.Add(property, null); } foreach (PropertyInfo pi in Framework.Do.GetProperties(m_Type)) { foreach (MoonSharpPropertyAttribute attr in pi.GetCustomAttributes(true).OfType()) { string name = attr.Name ?? pi.Name; if (m_PropertyMap.ContainsKey(name)) { throw new ArgumentException(string.Format("Type {0} has two definitions for MoonSharp property {1}", m_Type.FullName, name)); } else { m_PropertyMap.Add(name, pi); } } } } /// /// Adds an expected missing property, that is an expected field in the table with no corresponding property in the object. /// /// The name. public void AddExpectedMissingProperty(string name) { m_PropertyMap.Add(name, null); } private bool TryAssignProperty(object obj, string name, DynValue value) { if (m_PropertyMap.ContainsKey(name)) { PropertyInfo pi = m_PropertyMap[name]; if (pi != null) { object o; if (value.Type == DataType.Table && m_SubAssigners.ContainsKey(pi.PropertyType)) { var subassigner = m_SubAssigners[pi.PropertyType]; o = Activator.CreateInstance(pi.PropertyType); subassigner.AssignObjectUnchecked(o, value.Table); } else { o = Interop.Converters.ScriptToClrConversions.DynValueToObjectOfType(value, pi.PropertyType, null, false); } Framework.Do.GetSetMethod(pi).Invoke(obj, new object[] { o }); } return true; } return false; } private void AssignProperty(object obj, string name, DynValue value) { if (TryAssignProperty(obj, name, value)) return; if ((Script.GlobalOptions.FuzzySymbolMatching & FuzzySymbolMatchingBehavior.UpperFirstLetter) == FuzzySymbolMatchingBehavior.UpperFirstLetter && TryAssignProperty(obj, DescriptorHelpers.UpperFirstLetter(name), value)) return; if ((Script.GlobalOptions.FuzzySymbolMatching & FuzzySymbolMatchingBehavior.Camelify) == FuzzySymbolMatchingBehavior.Camelify && TryAssignProperty(obj, DescriptorHelpers.Camelify(name), value)) return; if ((Script.GlobalOptions.FuzzySymbolMatching & FuzzySymbolMatchingBehavior.PascalCase) == FuzzySymbolMatchingBehavior.PascalCase && TryAssignProperty(obj, DescriptorHelpers.UpperFirstLetter(DescriptorHelpers.Camelify(name)), value)) return; throw new ScriptRuntimeException("Invalid property {0}", name); } /// /// Assigns properties from tables to an object. /// /// The object. /// The table. /// Object is null /// The object is of an incompatible type. /// A field does not correspond to any property and that property is not one of the expected missing ones. public void AssignObject(object obj, Table data) { if (obj == null) throw new ArgumentNullException("Object is null"); if (!Framework.Do.IsInstanceOfType(m_Type, obj)) throw new ArgumentException(string.Format("Invalid type of object : got '{0}', expected {1}", obj.GetType().FullName, m_Type.FullName)); foreach (var pair in data.Pairs) { if (pair.Key.Type != DataType.String) { throw new ScriptRuntimeException("Invalid property of type {0}", pair.Key.Type.ToErrorTypeString()); } AssignProperty(obj, pair.Key.String, pair.Value); } } /// /// Sets the subassigner for the given type. Pass null to remove usage of subassigner for the given type. /// /// Type of the property for which the subassigner will be used. /// The property assigner. public void SetSubassignerForType(Type propertyType, IPropertyTableAssigner assigner) { if ( Framework.Do.IsAbstract(propertyType) || Framework.Do.IsGenericType(propertyType) || Framework.Do.IsInterface(propertyType) || Framework.Do.IsValueType(propertyType)) { throw new ArgumentException("propertyType must be a concrete, reference type"); } m_SubAssigners[propertyType] = assigner; } /// /// Assigns the properties of the specified object without checking the type. /// /// The object. /// The data. void IPropertyTableAssigner.AssignObjectUnchecked(object obj, Table data) { this.AssignObject(obj, data); } } /// /// Common interface for property assigners - basically used for sub-assigners /// public interface IPropertyTableAssigner { /// /// Assigns the properties of the specified object without checking the type. /// /// The object. /// The data. void AssignObjectUnchecked(object o, Table data); } }