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