context: #region Header
  /**
   * JsonMapper.cs
   *   JSON to .Net object and object to JSON conversions.
   *
   * The authors disclaim copyright to this source code. For more details, see
   * the COPYING file included with this distribution.
   **/
  #endregion
  
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Globalization;
  using System.IO;
  using System.Reflection;
  
  
  namespace LitJson
  {
      internal struct PropertyMetadata
      {
          public MemberInfo Info;
          public bool       IsField;
          public Type       Type;
      }
  
  
      internal struct ArrayMetadata
      {
          private Type element_type;
          private bool is_array;
          private bool is_list;
  
  
          public Type ElementType {
              get {
                  if (element_type == null)
                      return typeof (JsonData);
  
                  return element_type;
              }
  
              set { element_type = value; }
          }
  
          public bool IsArray {
              get { return is_array; }
              set { is_array = value; }
          }
  
          public bool IsList {
              get { return is_list; }
              set { is_list = value; }
          }
      }
  
  
      internal struct ObjectMetadata
      {
          private Type element_type;
          private bool is_dictionary;
  
          private IDictionary<string, PropertyMetadata> properties;
  
  
          public Type ElementType {
              get {
                  if (element_type == null)
                      return typeof (JsonData);
  
                  return element_type;
              }
  
              set { element_type = value; }
          }
  
          public bool IsDictionary {
              get { return is_dictionary; }
              set { is_dictionary = value; }
          }
  
          public IDictionary<string, PropertyMetadata> Properties {
              get { return properties; }
              set { properties = value; }
          }
      }
  
  
      internal delegate void ExporterFunc    (object obj, JsonWriter writer);
      public   delegate void ExporterFunc<T> (T obj, JsonWriter writer);
  
      internal delegate object ImporterFunc                (object input);
      public   delegate TValue ImporterFunc<TJson, TValue> (TJson input);
  
      public delegate IJsonWrapper WrapperFactory ();
  
  
      public class JsonMapper
      {
          #region Fields
          private static int max_nesting_depth;
  
          private static IFormatProvider datetime_format;
  
          private static IDictionary<Type, ExporterFunc> base_exporters_table;
          private static IDictionary<Type, ExporterFunc> custom_exporters_table;
  
          private static IDictionary<Type,
                  IDictionary<Type, ImporterFunc>> base_importers_table;
          private static IDictionary<Type,
                  IDictionary<Type, ImporterFunc>> custom_importers_table;
  
          private static IDictionary<Type, ArrayMetadata> array_metadata;
          private static readonly object array_metadata_lock = new Object ();
  
          private static IDictionary<Type,
                  IDictionary<Type, MethodInfo>> conv_ops;
          private static readonly object conv_ops_lock = new Object ();
  
          private static IDictionary<Type, ObjectMetadata> object_metadata;
          private static readonly object object_metadata_lock = new Object ();
  
          private static IDictionary<Type,
                  IList<PropertyMetadata>> type_properties;
          private static readonly object type_properties_lock = new Object ();
  
          private static JsonWriter      static_writer;
          private static readonly object static_writer_lock = new Object ();
          #endregion
  
  
          #region Constructors
          static JsonMapper ()
          {
              max_nesting_depth = 100;
  
              array_metadata = new Dictionary<Type, ArrayMetadata> ();
              conv_ops = new Dictionary<Type, IDictionary<Type, MethodInfo>> ();
              object_metadata = new Dictionary<Type, ObjectMetadata> ();
              type_properties = new Dictionary<Type,
                              IList<PropertyMetadata>> ();
  
              static_writer = new JsonWriter ();
  
              datetime_format = DateTimeFormatInfo.InvariantInfo;
  
              base_exporters_table   = new Dictionary<Type, ExporterFunc> ();
              custom_exporters_table = new Dictionary<Type, ExporterFunc> ();
  
              base_importers_table = new Dictionary<Type,
                                   IDictionary<Type, ImporterFunc>> ();
              custom_importers_table = new Dictionary<Type,
                                     IDictionary<Type, ImporterFunc>> ();
  
              RegisterBaseExporters ();
              RegisterBaseImporters ();
          }
          #endregion
  
  
          #region Private Methods
          private static void AddArrayMetadata (Type type)
          {
              if (array_metadata.ContainsKey (type))
                  return;
  
              ArrayMetadata data = new ArrayMetadata ();
  
              data.IsArray = type.IsArray;
  
              if (type.GetInterface ("System.Collections.IList") != null)
                  data.IsList = true;
  
              foreach (PropertyInfo p_info in type.GetProperties ()) {
                  if (p_info.Name != "Item")
                      continue;
  
                  ParameterInfo[] parameters = p_info.GetIndexParameters ();
  
                  if (parameters.Length != 1)
                      continue;
  
                  if (parameters[0].ParameterType == typeof (int))
                      data.ElementType = p_info.PropertyType;
              }
  
              lock (array_metadata_lock) {
                  try {
                      array_metadata.Add (type, data);
                  } catch (ArgumentException) {
                      return;
                  }
              }
          }
  
          private static void AddObjectMetadata (Type type)
          {
              if (object_metadata.ContainsKey (type))
                  return;
  
              ObjectMetadata data = new ObjectMetadata ();
  
              if (type.GetInterface ("System.Collections.IDictionary") != null)
                  data.IsDictionary = true;
  
              data.Properties = new Dictionary<string, PropertyMetadata> ();
  
              foreach (PropertyInfo p_info in type.GetProperties ()) {
                  if (p_info.Name == "Item") {
                      ParameterInfo[] parameters = p_info.GetIndexParameters ();
  
                      if (parameters.Length != 1)
                          continue;
  
                      if (parameters[0].ParameterType == typeof (string))
                          data.ElementType = p_info.PropertyType;
  
                      continue;
                  }
  
                  PropertyMetadata p_data = new PropertyMetadata ();
                  p_data.Info = p_info;
                  p_data.Type = p_info.PropertyType;
  
                  data.Properties.Add (p_info.Name, p_data);
              }
  
              foreach (FieldInfo f_info in type.GetFields ()) {
                  PropertyMetadata p_data = new PropertyMetadata ();
                  p_data.Info = f_info;
                  p_data.IsField = true;
                  p_data.Type = f_info.FieldType;
  
                  data.Properties.Add (f_info.Name, p_data);
              }
  
              lock (object_metadata_lock) {
                  try {
                      object_metadata.Add (type, data);
                  } catch (ArgumentException) {
                      return;
                  }
              }
          }
  
          private static void AddTypeProperties (Type type)
          {
              if (type_properties.ContainsKey (type))
                  return;
  
              IList<PropertyMetadata> props = new List<PropertyMetadata> ();
  
              foreach (PropertyInfo p_info in type.GetProperties ()) {
                  if (p_info.Name == "Item")
                      continue;
  
                  PropertyMetadata p_data = new PropertyMetadata ();
                  p_data.Info = p_info;
                  p_data.IsField = false;
                  props.Add (p_data);
              }
  
              foreach (FieldInfo f_info in type.GetFields ()) {
  
gt:                 PropertyMetadata p_data = new PropertyMetadata ();