using System; using System.Collections.Generic; using System.Reflection; namespace Emby.Server.Implementations.Services { /// /// Serializer cache of delegates required to create a type from a string map (e.g. for REST urls) /// public class StringMapTypeDeserializer { internal class PropertySerializerEntry { public PropertySerializerEntry(Action propertySetFn, Func propertyParseStringFn, Type propertyType) { PropertySetFn = propertySetFn; PropertyParseStringFn = propertyParseStringFn; PropertyType = PropertyType; } public Action PropertySetFn { get; private set; } public Func PropertyParseStringFn { get; private set; } public Type PropertyType { get; private set; } } private readonly Type type; private readonly Dictionary propertySetterMap = new Dictionary(StringComparer.OrdinalIgnoreCase); public Func GetParseFn(Type propertyType) { if (propertyType == typeof(string)) { return s => s; } return _GetParseFn(propertyType); } private readonly Func _CreateInstanceFn; private readonly Func> _GetParseFn; public StringMapTypeDeserializer(Func createInstanceFn, Func> getParseFn, Type type) { _CreateInstanceFn = createInstanceFn; _GetParseFn = getParseFn; this.type = type; foreach (var propertyInfo in RestPath.GetSerializableProperties(type)) { var propertySetFn = TypeAccessor.GetSetPropertyMethod(propertyInfo); var propertyType = propertyInfo.PropertyType; var propertyParseStringFn = GetParseFn(propertyType); var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn, propertyType); propertySetterMap[propertyInfo.Name] = propertySerializer; } } public object PopulateFromMap(object instance, IDictionary keyValuePairs) { PropertySerializerEntry propertySerializerEntry = null; if (instance == null) { instance = _CreateInstanceFn(type); } foreach (var pair in keyValuePairs) { string propertyName = pair.Key; string propertyTextValue = pair.Value; if (string.IsNullOrEmpty(propertyTextValue) || !propertySetterMap.TryGetValue(propertyName, out propertySerializerEntry) || propertySerializerEntry.PropertySetFn == null) { continue; } if (propertySerializerEntry.PropertyType == typeof(bool)) { //InputExtensions.cs#530 MVC Checkbox helper emits extra hidden input field, generating 2 values, first is the real value propertyTextValue = LeftPart(propertyTextValue, ','); } var value = propertySerializerEntry.PropertyParseStringFn(propertyTextValue); if (value == null) { continue; } propertySerializerEntry.PropertySetFn(instance, value); } return instance; } public static string LeftPart(string strVal, char needle) { if (strVal == null) { return null; } var pos = strVal.IndexOf(needle); return pos == -1 ? strVal : strVal.Substring(0, pos); } } internal static class TypeAccessor { public static Action GetSetPropertyMethod(PropertyInfo propertyInfo) { if (!propertyInfo.CanWrite || propertyInfo.GetIndexParameters().Length > 0) { return null; } var setMethodInfo = propertyInfo.SetMethod; return (instance, value) => setMethodInfo.Invoke(instance, new[] { value }); } } }