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);
}
}