using System; using System.Collections.Generic; using LeanCloud.Storage.Internal; namespace LeanCloud.Utilities { /// /// A set of utilities for converting generic types between each other. /// public static class Conversion { /// /// Converts a value to the requested type -- coercing primitives to /// the desired type, wrapping lists and dictionaries appropriately, /// or else returning null. /// /// This should be used on any containers that might be coming from a /// user to normalize the collection types. Collection types coming from /// JSON deserialization can be safely assumed to be lists or dictionaries of /// objects. /// public static T As(object value) where T : class { return ConvertTo(value) as T; } /// /// Converts a value to the requested type -- coercing primitives to /// the desired type, wrapping lists and dictionaries appropriately, /// or else throwing an exception. /// /// This should be used on any containers that might be coming from a /// user to normalize the collection types. Collection types coming from /// JSON deserialization can be safely assumed to be lists or dictionaries of /// objects. /// public static T To(object value) { return (T)ConvertTo(value); } /// /// Converts a value to the requested type -- coercing primitives to /// the desired type, wrapping lists and dictionaries appropriately, /// or else passing the object along to the caller unchanged. /// /// This should be used on any containers that might be coming from a /// user to normalize the collection types. Collection types coming from /// JSON deserialization can be safely assumed to be lists or dictionaries of /// objects. /// internal static object ConvertTo(object value) { if (value is T || value == null) { return value; } if (ReflectionHelpers.IsPrimitive(typeof(T))) { return (T)Convert.ChangeType(value, typeof(T)); } if (ReflectionHelpers.IsConstructedGenericType(typeof(T))) { // Add lifting for nullables. Only supports conversions between primitives. if (ReflectionHelpers.IsNullable(typeof(T))) { var innerType = ReflectionHelpers.GetGenericTypeArguments(typeof(T))[0]; if (ReflectionHelpers.IsPrimitive(innerType)) { return (T)Convert.ChangeType(value, innerType); } } Type listType = GetInterfaceType(value.GetType(), typeof(IList<>)); var la = typeof(T).GetGenericTypeDefinition(); var ilb = typeof(IList<>); var lb = typeof(List<>); if (listType != null && (la == ilb || la == lb)) { var wrapperType = typeof(FlexibleListWrapper<,>) .MakeGenericType(ReflectionHelpers.GetGenericTypeArguments(typeof(T))[0], ReflectionHelpers.GetGenericTypeArguments(listType)[0]); return Activator.CreateInstance(wrapperType, value); } Type dictType = GetInterfaceType(value.GetType(), typeof(IDictionary<,>)); var da = typeof(T).GetGenericTypeDefinition(); var db = typeof(IDictionary<,>); if (dictType != null && da == db) { var wrapperType = typeof(FlexibleDictionaryWrapper<,>) .MakeGenericType(ReflectionHelpers.GetGenericTypeArguments(typeof(T))[1], ReflectionHelpers.GetGenericTypeArguments(dictType)[1]); return Activator.CreateInstance(wrapperType, value); } } return value; } /// /// Holds a dictionary that maps a cache of interface types for related concrete types. /// The lookup is slow the first time for each type because it has to enumerate all interface /// on the object type, but made fast by the cache. /// /// The map is: /// (object type, generic interface type) => constructed generic type /// private static readonly Dictionary, Type> interfaceLookupCache = new Dictionary, Type>(); private static Type GetInterfaceType(Type objType, Type genericInterfaceType) { // Side note: It so sucks to have to do this. What a piece of crap bit of code // Unfortunately, .NET doesn't provide any of the right hooks to do this for you // *sigh* if (ReflectionHelpers.IsConstructedGenericType(genericInterfaceType)) { genericInterfaceType = genericInterfaceType.GetGenericTypeDefinition(); } var cacheKey = new Tuple(objType, genericInterfaceType); if (interfaceLookupCache.ContainsKey(cacheKey)) { return interfaceLookupCache[cacheKey]; } foreach (var type in ReflectionHelpers.GetInterfaces(objType)) { if (ReflectionHelpers.IsConstructedGenericType(type) && type.GetGenericTypeDefinition() == genericInterfaceType) { return interfaceLookupCache[cacheKey] = type; } } return null; } } }