context: #region License
  // Copyright (c) 2007 James Newton-King
  //
  // Permission is hereby granted, free of charge, to any person
  // obtaining a copy of this software and associated documentation
  // files (the "Software"), to deal in the Software without
  // restriction, including without limitation the rights to use,
  // copy, modify, merge, publish, distribute, sublicense, and/or sell
  // copies of the Software, and to permit persons to whom the
  // Software is furnished to do so, subject to the following
  // conditions:
  //
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  // OTHER DEALINGS IN THE SOFTWARE.
  #endregion
  
  using System;
  using System.Collections.Generic;
  #if !(PORTABLE || PORTABLE40 || NET35 || NET20)
  using System.Numerics;
  #endif
  using System.Reflection;
  using System.Collections;
  using System.Globalization;
  using System.Runtime.Serialization;
  using System.Runtime.Serialization.Formatters;
  using System.Text;
  #if NET20
  using Newtonsoft.Json.Utilities.LinqBridge;
  #else
  using System.Linq;
  #endif
  using Newtonsoft.Json.Serialization;
  
  namespace Newtonsoft.Json.Utilities
  {
  #if (NETFX_CORE || PORTABLE || PORTABLE40)
      internal enum MemberTypes
      {
          Property,
          Field,
          Event,
          Method,
          Other
      }
  #endif
  
  #if NETFX_CORE || PORTABLE
      [Flags]
      internal enum BindingFlags
      {
          Default = 0,
          IgnoreCase = 1,
          DeclaredOnly = 2,
          Instance = 4,
          Static = 8,
          Public = 16,
          NonPublic = 32,
          FlattenHierarchy = 64,
          InvokeMethod = 256,
          CreateInstance = 512,
          GetField = 1024,
          SetField = 2048,
          GetProperty = 4096,
          SetProperty = 8192,
          PutDispProperty = 16384,
          ExactBinding = 65536,
          PutRefDispProperty = 32768,
          SuppressChangeType = 131072,
          OptionalParamBinding = 262144,
          IgnoreReturn = 16777216
      }
  #endif
  
      internal static class ReflectionUtils
      {
          public static readonly Type[] EmptyTypes;
  
          static ReflectionUtils()
          {
  #if !(NETFX_CORE || PORTABLE40 || PORTABLE)
              EmptyTypes = Type.EmptyTypes;
  #else
              EmptyTypes = new Type[0];
  #endif
          }
  
          public static bool IsVirtual(this PropertyInfo propertyInfo)
          {
              ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo");
  
              MethodInfo m = propertyInfo.GetGetMethod();
              if (m != null && m.IsVirtual)
                  return true;
  
              m = propertyInfo.GetSetMethod();
              if (m != null && m.IsVirtual)
                  return true;
  
              return false;
          }
  
          public static MethodInfo GetBaseDefinition(this PropertyInfo propertyInfo)
          {
              ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo");
  
              MethodInfo m = propertyInfo.GetGetMethod();
              if (m != null)
                  return m.GetBaseDefinition();
  
              m = propertyInfo.GetSetMethod();
              if (m != null)
                  return m.GetBaseDefinition();
  
              return null;
          }
  
          public static bool IsPublic(PropertyInfo property)
          {
              if (property.GetGetMethod() != null && property.GetGetMethod().IsPublic)
                  return true;
              if (property.GetSetMethod() != null && property.GetSetMethod().IsPublic)
                  return true;
  
              return false;
          }
  
          public static Type GetObjectType(object v)
          {
              return (v != null) ? v.GetType() : null;
          }
  
          public static string GetTypeName(Type t, FormatterAssemblyStyle assemblyFormat, SerializationBinder binder)
          {
              string fullyQualifiedTypeName;
  #if !(NET20 || NET35)
              if (binder != null)
              {
                  string assemblyName, typeName;
                  binder.BindToName(t, out assemblyName, out typeName);
                  fullyQualifiedTypeName = typeName + (assemblyName == null ? "" : ", " + assemblyName);
              }
              else
              {
                  fullyQualifiedTypeName = t.AssemblyQualifiedName;
              }
  #else
              fullyQualifiedTypeName = t.AssemblyQualifiedName;
  #endif
  
              switch (assemblyFormat)
              {
                  case FormatterAssemblyStyle.Simple:
                      return RemoveAssemblyDetails(fullyQualifiedTypeName);
                  case FormatterAssemblyStyle.Full:
                      return fullyQualifiedTypeName;
                  default:
                      throw new ArgumentOutOfRangeException();
              }
          }
  
          private static string RemoveAssemblyDetails(string fullyQualifiedTypeName)
          {
              StringBuilder builder = new StringBuilder();
  
              // loop through the type name and filter out qualified assembly details from nested type names
              bool writingAssemblyName = false;
              bool skippingAssemblyDetails = false;
              for (int i = 0; i < fullyQualifiedTypeName.Length; i++)
              {
                  char current = fullyQualifiedTypeName[i];
                  switch (current)
                  {
                      case '[':
                          writingAssemblyName = false;
                          skippingAssemblyDetails = false;
                          builder.Append(current);
                          break;
                      case ']':
                          writingAssemblyName = false;
                          skippingAssemblyDetails = false;
                          builder.Append(current);
                          break;
                      case ',':
                          if (!writingAssemblyName)
                          {
                              writingAssemblyName = true;
                              builder.Append(current);
                          }
                          else
                          {
                              skippingAssemblyDetails = true;
                          }
                          break;
                      default:
                          if (!skippingAssemblyDetails)
                              builder.Append(current);
                          break;
                  }
              }
  
              return builder.ToString();
          }
  
          public static bool HasDefaultConstructor(Type t, bool nonPublic)
          {
              ValidationUtils.ArgumentNotNull(t, "t");
  
              if (t.IsValueType())
                  return true;
  
              return (GetDefaultConstructor(t, nonPublic) != null);
          }
  
          public static ConstructorInfo GetDefaultConstructor(Type t)
          {
              return GetDefaultConstructor(t, false);
          }
  
          public static ConstructorInfo GetDefaultConstructor(Type t, bool nonPublic)
          {
              BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public;
              if (nonPublic)
                  bindingFlags = bindingFlags | BindingFlags.NonPublic;
  
              return t.GetConstructors(bindingFlags).SingleOrDefault(c => !c.GetParameters().Any());
          }
  
          public static bool IsNullable(Type t)
          {
              ValidationUtils.ArgumentNotNull(t, "t");
  
              if (t.IsValueType())
                  return IsNullableType(t);
  
              return true;
          }
  
          public static bool IsNullableType(Type t)
          {
              ValidationUtils.ArgumentNotNull(t, "t");
  
              return (t.IsGenericType() && t.GetGenericTypeDefinition() == typeof(Nullable<>));
          }
  
          public static Type EnsureNotNullableType(Type t)
          {
              return (IsNullableType(t))
                  ? Nullable.GetUnderlyingType(t)
                  : t;
          }
  
          public static bool IsGenericDefinition(Type type, Type genericInterfaceDefinition)
          {
              if (!type.IsGenericType())
                  return false;
  
              Type t = type.GetGenericTypeDefinition();
              return (t == genericInterfaceDefinition);
          }
  
          public static bool ImplementsGenericDefinition(Type type, Type genericInterfaceDefinition)
          {
              Type implementingType;
              return ImplementsGenericDefinition(type, genericInterfaceDefinition, out implementingType);
          }
  
          public static bool ImplementsGenericDefinition(Type type, Type genericInterfaceDefinition, out Type implementingType)
          {
              ValidationUtils.ArgumentNotNull(type, "type");
              ValidationUtils.ArgumentNotNull(genericInterfaceDefinition, "genericInterfaceDefinition");
  
              if (!genericInterfaceDefinition.IsInterface() || !genericInterfaceDefinition.IsGenericTypeDefinition())
                  throw new ArgumentNullException("'{0}' is not a generic interface definition.".FormatWith(CultureInfo.InvariantCulture, genericInterfaceDefinition));
  
              if (type.IsInterface())
              {
                  if (type.IsGenericType())
                  {
                      Type interfaceDefinition = type.GetGenericTypeDefinition();
  
                      if (genericInterfaceDefinition == interfaceDefinition)
                      {
                          implementingType = type;
                          return true;
                      }
                  }
              }
  
              foreach (Type i in type.GetInterfaces())
              {
                  if (i.IsGenericType())
                  {
                      Type interfaceDefinition = i.GetGenericTypeDefinition();
  
                      if (genericInterfaceDefinition == interfaceDefinition)
                      {
                          implementingType = i;
                          return true;
                      }
                  }
              }
  
              implementingType = null;
              return false;
          }
  
          public static bool InheritsGenericDefinition(Type type, Type genericClassDefinition)
          {
              Type implementingType;
              return InheritsGenericDefinition(type, genericClassDefinition, out implementingType);
          }
  
          public static bool InheritsGenericDefinition(Type type, Type genericClassDefinition, out Type implementingType)
          {
              ValidationUtils.ArgumentNotNull(type, "type");
              ValidationUtils.ArgumentNotNull(genericClassDefinition, "genericClassDefinition");
  
              if (!genericClassDefinition.IsClass() || !genericClassDefinition.IsGenericTypeDefinition())
                  throw new ArgumentNullException("'{0}' is not a generic class definition.".FormatWith(CultureInfo.InvariantCulture, genericClassDefinition));
  
              return InheritsGenericDefinitionInternal(type, genericClassDefinition, out implementingType);
          }
  
          private static bool InheritsGenericDefinitionInternal(Type currentType, Type genericClassDefinition, out Type implementingType)
          {
              if (currentType.IsGenericType())
              {
                  Type currentGenericClassDefinition = currentType.GetGenericTypeDefinition();
  
                  if (genericClassDefinition == currentGenericClassDefinition)
                  {
                      implementingType = currentType;
                      return true;
                  }
              }
  
              if (currentType.BaseType() == null)
              {
                  implementingType = null;
                  return false;
              }
  
              return InheritsGenericDefinitionInternal(currentType.BaseType(), genericClassDefinition, out implementingType);
          }
  
          /// <summary>
          /// Gets the type of the typed collection's items.
          /// </summary>
          /// <param name="type">The type.</param>
          /// <returns>The type of the typed collection's items.</returns>
          public static Type GetCollectionItemType(Type type)
          {
              ValidationUtils.ArgumentNotNull(type, "type");
              Type genericListType;
  
              if (type.IsArray)
              {
                  return type.GetElementType();
              }
              if (ImplementsGenericDefinition(type, typeof(IEnumerable<>), out genericListType))
              {
                  if (genericListType.IsGenericTypeDefinition())
                      throw new Exception("Type {0} is not a collection.".FormatWith(CultureInfo.InvariantCulture, type));
  
                  return genericListType.GetGenericArguments()[0];
              }
              if (typeof(IEnumerable).IsAssignableFrom(type))
              {
                  return null;
              }
              
              throw new Exception("Type {0} is not a collection.".FormatWith(CultureInfo.InvariantCulture, type));
          }
  
          public static void GetDictionaryKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType)
          {
              ValidationUtils.ArgumentNotNull(dictionaryType, "type");
  
              Type genericDictionaryType;
              if (ImplementsGenericDefinition(dictionaryType, typeof(IDictionary<,>), out genericDictionaryType))
              {
                  if (genericDictionaryType.IsGenericTypeDefinition())
                      throw new Exception("Type {0} is not a dictionary.".FormatWith(CultureInfo.InvariantCulture, dictionaryType));
  
                  Type[] dictionaryGenericArguments = genericDictionaryType.GetGenericArguments();
  
                  keyType = dictionaryGenericArguments[0];
                  valueType = dictionaryGenericArguments[1];
                  return;
              }
              if (typeof(IDictionary).IsAssignableFrom(dictionaryType))
              {
                  keyType = null;
                  valueType = null;
                  return;
              }
  
              throw new Exception("Type {0} is not a dictionary.".FormatWith(CultureInfo.InvariantCulture, dictionaryType));
          }
  
          /// <summary>
          /// Gets the member's underlying type.
          /// </summary>
          /// <param name="member">The member.</param>
          /// <returns>The underlying type of the member.</returns>
          public static Type GetMemberUnderlyingType(MemberInfo member)
          {
              ValidationUtils.ArgumentNotNull(member, "member");
  
              switch (member.MemberType())
              {
                  case MemberTypes.Field:
                      return ((FieldInfo)member).FieldType;
                  case MemberTypes.Property:
                      return ((PropertyInfo)member).PropertyType;
                  case MemberTypes.Event:
                      return ((EventInfo)member).EventHandlerType;
                  case MemberTypes.Method:
                      return ((MethodInfo)member).ReturnType;
                  default:
                      throw new ArgumentException("MemberInfo must be of type FieldInfo, PropertyInfo, EventInfo or MethodInfo", "member");
              }
          }
  
          /// <summary>
          /// Determines whether the member is an indexed property.
          /// </summary>
          /// <param name="member">The member.</param>
          /// <returns>
          /// 	<c>true</c> if the member is an indexed property; otherwise, <c>false</c>.
          /// </returns>
          public static bool IsIndexedProperty(MemberInfo member)
          {
              ValidationUtils.ArgumentNotNull(member, "member");
  
              PropertyInfo propertyInfo = member as PropertyInfo;
  
              if (propertyInfo != null)
                  return IsIndexedProperty(propertyInfo);
              else
                  return false;
          }
  
          /// <summary>
          /// Determines whether the property is an indexed property.
          /// </summary>
          /// <param name="property">The property.</param>
          /// <returns>
          /// 	<c>true</c> if the property is an indexed property; otherwise, <c>false</c>.
          /// </returns>
          public static bool IsIndexedProperty(PropertyInfo property)
          {
              ValidationUtils.ArgumentNotNull(property, "property");
  
              return (property.GetIndexParameters().Length > 0);
          }
  
          /// <summary>
          /// Gets the member's value on the object.
          /// </summary>
          /// <param name="member">The member.</param>
          /// <param name="target">The target object.</param>
          /// <returns>The member's value on the object.</returns>
          public static object GetMemberValue(MemberInfo member, object target)
          {
              ValidationUtils.ArgumentNotNull(member, "member");
              ValidationUtils.ArgumentNotNull(target, "target");
  
              switch (member.MemberType())
              {
                  case MemberTypes.Field:
                      return ((FieldInfo)member).GetValue(target);
                  case MemberTypes.Property:
                      try
                      {
                          return ((PropertyInfo)member).GetValue(target, null);
                      }
                      catch (TargetParameterCountException e)
                      {
                          throw new ArgumentException("MemberInfo '{0}' has index parameters".FormatWith(CultureInfo.InvariantCulture, member.Name), e);
                      }
                  default:
                      throw new ArgumentException("MemberInfo '{0}' is not of type FieldInfo or PropertyInfo".FormatWith(CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, member.Name), "member");
              }
          }
  
          /// <summary>
          /// Sets the member's value on the target object.
          /// </summary>
          /// <param name="member">The member.</param>
          /// <param name="target">The target.</param>
          /// <param name="value">The value.</param>
          public static void SetMemberValue(MemberInfo member, object target, object value)
          {
              ValidationUtils.ArgumentNotNull(member, "member");
              ValidationUtils.ArgumentNotNull(target, "target");
  
              switch (member.MemberType())
              {
                  case MemberTypes.Field:
                      ((FieldInfo)member).SetValue(target, value);
                      break;
                  case MemberTypes.Property:
                      ((PropertyInfo)member).SetValue(target, value, null);
                      break;
                  default:
                      throw new ArgumentException("MemberInfo '{0}' must be of type FieldInfo or PropertyInfo".FormatWith(CultureInfo.InvariantCulture, member.Name), "member");
              }
          }
  
          /// <summary>
          /// Determines whether the specified MemberInfo can be read.
          /// </summary>
          /// <param name="member">The MemberInfo to determine whether can be read.</param>
          /// /// <param name="nonPublic">if set to <c>true</c> then allow the member to be gotten non-publicly.</param>
          /// <returns>
          /// 	<c>true</c> if the specified MemberInfo can be read; otherwise, <c>false</c>.
          /// </returns>
          public static bool CanReadMemberValue(MemberInfo member, bool nonPublic)
          {
              switch (member.MemberType())
              {
                  case MemberTypes.Field:
                      FieldInfo fieldInfo = (FieldInfo)member;
  
                      if (nonPublic)
                          return true;
                      else if (fieldInfo.IsPublic)
                          return true;
                      return false;
                  case MemberTypes.Property:
                      PropertyInfo propertyInfo = (PropertyInfo)member;
  
                      if (!propertyInfo.CanRead)
                          return false;
                      if (nonPublic)
                          return true;
                      return (propertyInfo.GetGetMethod(nonPublic) != null);
                  default:
                      return false;
              }
          }
  
          /// <summary>
          /// Determines whether the specified MemberInfo can be set.
          /// </summary>
          /// <param name="member">The MemberInfo to determine whether can be set.</param>
          /// <param name="nonPublic">if set to <c>true</c> then allow the member to be set non-publicly.</param>
          /// <param name="canSetReadOnly">if set to <c>true</c> then allow the member to be set if read-only.</param>
          /// <returns>
          /// 	<c>true</c> if the specified MemberInfo can be set; otherwise, <c>false</c>.
          /// </returns>
          public static bool CanSetMemberValue(MemberInfo member, bool nonPublic, bool canSetReadOnly)
          {
              switch (member.MemberType())
              {
                  case MemberTypes.Field:
                      FieldInfo fieldInfo = (FieldInfo)member;
  
                      if (fieldInfo.IsInitOnly && !canSetReadOnly)
                          return false;
                      if (nonPublic)
                          return true;
                      else if (fieldInfo.IsPublic)
                          return true;
                      return false;
                  case MemberTypes.Property:
                      PropertyInfo propertyInfo = (PropertyInfo)member;
  
                      if (!propertyInfo.CanWrite)
                          return false;
                      if (nonPublic)
                          return true;
                      return (propertyInfo.GetSetMethod(nonPublic) != null);
                  default:
                      return false;
              }
          }
  
          public static List<MemberInfo> GetFieldsAndProperties(Type type, BindingFlags bindingAttr)
          {
              List<MemberInfo> targetMembers = new List<MemberInfo>();
  
              targetMembers.AddRange(GetFields(type, bindingAttr));
              targetMembers.AddRange(GetProperties(type, bindingAttr));
  
              // for some reason .NET returns multiple members when overriding a generic member on a base class
              // http://social.msdn.microsoft.com/Forums/en-US/b5abbfee-e292-4a64-8907-4e3f0fb90cd9/reflection-overriden-abstract-generic-properties?forum=netfxbcl
              // filter members to only return the override on the topmost class
              // update: I think this is fixed in .NET 3.5 SP1 - leave this in for now...
              List<MemberInfo> distinctMembers = new List<MemberInfo>(targetMembers.Count);
  
              foreach (var groupedMember in targetMembers.GroupBy(m => m.Name))
              {
                  int count = groupedMember.Count();
                  IList<MemberInfo> members = groupedMember.ToList();
  
                  if (count == 1)
                  {
                      distinctMembers.Add(members.First());
                  }
                  else
                  {
                      IList<MemberInfo> resolvedMembers = new List<MemberInfo>();
                      foreach (MemberInfo memberInfo in members)
                      {
                          // this is a bit hacky
                          // if the hiding property is hiding a base property and it is virtual
                          // then this ensures the derived property gets used
                          if (resolvedMembers.Count == 0)
                              resolvedMembers.Add(memberInfo);
                          else if (!IsOverridenGenericMember(memberInfo, bindingAttr) || memberInfo.Name == "Item")
                              resolvedMembers.Add(memberInfo);
                      }
  
                      distinctMembers.AddRange(resolvedMembers);
                  }
              }
  
              return distinctMembers;
          }
  
          private static bool IsOverridenGenericMember(MemberInfo memberInfo, BindingFlags bindingAttr)
          {
              if (memberInfo.MemberType() != MemberTypes.Property)
                  return false;
  
              PropertyInfo propertyInfo = (PropertyInfo)memberInfo;
              if (!IsVirtual(propertyInfo))
                  return false;
  
              Type declaringType = propertyInfo.DeclaringType;
              if (!declaringType.IsGenericType())
                  return false;
              Type genericTypeDefinition = declaringType.GetGenericTypeDefinition();
              if (genericTypeDefinition == null)
                  return false;
              MemberInfo[] members = genericTypeDefinition.GetMember(propertyInfo.Name, bindingAttr);
              if (members.Length == 0)
                  return false;
  
gt:             Type memberUnderlyingType = GetMemberUnderlyingType(members[0]);