private/runtime/Helpers/TypeDetails.cs

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;

namespace Carbon.Json
{
    using Converters;
    using Internal.Extensions;

    internal class TypeDetails
    {
        private readonly Type info;

        public TypeDetails(Type info)
        {
            this.info = info ?? throw new ArgumentNullException(nameof(info));
        }

        public Type NonNullType { get; set; }

        public object DefaultValue { get; set; }

        public bool IsNullable { get; set; }

        public bool IsList { get; set; }

        public bool IsStringLike { get; set; }

        public bool IsEnum => info.IsEnum;

        public bool IsArray => info.IsArray;

        public bool IsValueType => info.IsValueType;

        public Type ElementType { get; set; }

        public IJsonConverter JsonConverter { get; set; }

        #region Creation

        private static readonly ConcurrentDictionary<Type, TypeDetails> cache = new ConcurrentDictionary<Type, TypeDetails>();

        public static TypeDetails Get<T>() => Get(typeof(T));

        public static TypeDetails Get(Type type) => cache.GetOrAdd(type, Create);

        private static TypeDetails Create(Type type)
        {
            var isGenericList = !type.IsPrimitive && type.ImplementsOpenGenericInterface(typeof(IList<>));
            var isList = !type.IsPrimitive && (isGenericList || typeof(IList).IsAssignableFrom(type));

            var isNullable = type.IsNullable();

            Type elementType;

            if (type.IsArray)
            {
                elementType = type.GetElementType();
            }
            else if (isGenericList)
            {
                var iList = type.GetOpenGenericInterface(typeof(IList<>));

                elementType = iList.GetGenericArguments()[0];
            }
            else
            {
                elementType = null;
            }

            var nonNullType = isNullable ? type.GetGenericArguments()[0] : type;

            var isStringLike = false;

            IJsonConverter converter;

            var jsonConverterAttribute = type.GetCustomAttribute<JsonConverterAttribute>();

            if (jsonConverterAttribute != null)
            {
                converter = jsonConverterAttribute.Converter;
            }
            else if (nonNullType.IsEnum)
            {
                converter = new EnumConverter(nonNullType);
            }
            else if (JsonConverterFactory.Instances.TryGetValue(nonNullType, out converter))
            {
            }
            else if (StringLikeHelper.IsStringLike(nonNullType))
            {
                isStringLike = true;

                converter = new StringLikeConverter(nonNullType);
            }

            return new TypeDetails(nonNullType) {
                NonNullType = nonNullType,
                DefaultValue = type.IsValueType ? Activator.CreateInstance(type) : null,
                IsNullable = isNullable,
                IsList = isList,
                IsStringLike = isStringLike,
                ElementType = elementType,
                JsonConverter = converter
            };
        }

        #endregion
    }
}