<commit_before>﻿#region Apache License 2.0

// Copyright 2015 <NAME>
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml;
using Fasterflect;
using Org.Edgerunner.DotSerialize.Exceptions;
using Org.Edgerunner.DotSerialize.Reflection;
using Org.Edgerunner.DotSerialize.Reflection.Construction;
using Org.Edgerunner.DotSerialize.Serialization.Factories;
using Org.Edgerunner.DotSerialize.Serialization.Reference;

namespace Org.Edgerunner.DotSerialize.Serialization
{
   public class DefaultTypeSerializer
   {
      protected ITypeSerializerFactory Factory { get; set; }
      protected IReferenceManager RefManager { get; set; }
      public ITypeInspector TypeInspector { get; set; }

      /// <summary>
      ///    Initializes a new instance of the <see cref="GenericTypeSerializer" /> class.
      /// </summary>
      /// <param name="factory"></param>
      /// <param name="referenceManager"></param>
      /// <param name="typeInspector"></param>
      public DefaultTypeSerializer(ITypeSerializerFactory factory, IReferenceManager referenceManager, ITypeInspector typeInspector)
      {
         Factory = factory;
         RefManager = referenceManager;
         TypeInspector = typeInspector;
      }

      public virtual T Deserialize<T>(XmlReader reader)
      {
         if (reader == null) throw new ArgumentNullException("reader");
         return (T)Deserialize(reader, typeof(T));
      }

      public virtual object Deserialize(XmlReader reader, Type type)
      {
         if (reader == null) throw new ArgumentNullException("reader");
         if (type == null) throw new ArgumentNullException("type");
         object result = null;
         // Handle attributes
         if ((reader.NodeType == XmlNodeType.Attribute))
         {
            if (!((TypeHelper.IsPrimitive(type)) || (TypeHelper.IsEnum(type))))
               throw new SerializationException("Only primitives or enums can be stored in attributes.");

            if (TypeHelper.IsPrimitive(type))
               result = DeserializePrimitive(type, reader);
            else if (TypeHelper.IsEnum(type))
               result = DeserializeEnum(type, reader);
            else
               throw new SerializationException(string.Format("Unable to deserialize unexpected type \"{0}\" from an attribute.", type.Name()));
            return result;
         }
         // Handle Elements
         if (reader.NodeType == XmlNodeType.Element)
         {
            if (TypeHelper.IsPrimitive(type))
               result = DeserializePrimitive(type, reader);
            else if (TypeHelper.IsArray(type))
               result = DeserializeArray(type, reader);
            else if (TypeHelper.IsEnum(type))
               result = DeserializeEnum(type, reader);
            else
            {
               // Class or struct
               if (TypeHelper.ReferenceIsNull(reader))
                  return null;

               var memberValues = new Dictionary<TypeMemberInfo, object>();
               RefManager.StartLateBindingCapture(type);
               var info = TypeInspector.GetInfo(type);

               // read attributes
               while (reader.MoveToNextAttribute())
                  DeserializeAttribMember(reader, info, memberValues);
               reader.MoveToContent();

               if (!reader.IsEmptyElement)
                  // read child elements
                  // ReSharper disable once LoopVariableIsNeverChangedInsideLoop
                  while (ReadNextElement(reader))
                     if (reader.NodeType == XmlNodeType.Element)
                        DeserializeElementMember(reader, info, memberValues);

               result = TypeFactory.CreateInstance(type, memberValues);
               if (result == null)
                  throw new SerializationException(string.Format("Unable to create an instance of type \"{0}\".", type.Name()));

               RefManager.FinishCaptures(result);
            }

            // hand back our new object          
            return result;
         }
         throw new SerializationException(
            string.Format("Reader was not positioned on a node of type Attribute or Element.\r\n" +
                          "A custom type serializer probably positioned the reader incorrectly." +
                          "Unable to deserialize type \"{0}\".",
                          type.Name()));
      }

      protected virtual void DeserializeAttribMember(XmlReader reader,
                                                  TypeInfo info,
                                                  Dictionary<TypeMemberInfo, object> memberValues)
      {
         if (reader == null) throw new ArgumentNullException("reader");
         if (info == null) throw new ArgumentNullException("info");
         if (memberValues == null) throw new ArgumentNullException("memberValues");
         if (!info.MemberInfoByEntityName.ContainsKey(reader.Name))
            return;
         if (!info.MemberInfoByEntityName[reader.Name].IsAttribute)
            return;
         var memberInfo = info.MemberInfoByEntityName[reader.Name];
         // Attempt to fetch a custom type serializer
         ITypeSerializer typeSerializer =
            Factory.CallMethod(new[] { memberInfo.DataType }, "GetTypeSerializer") as ITypeSerializer;
         if (typeSerializer != null)
            memberValues[memberInfo] = typeSerializer.Deserialize(reader, memberInfo.DataType);
         else
         // Since there was no bound custom type serializer we default to the GenericTypeSerializer
         {
            var defaultSerializer = Factory.GetDefaultSerializer();
            memberValues[memberInfo] = defaultSerializer.Deserialize(reader, memberInfo.DataType);
         }
      }

      protected virtual void DeserializeElementMember(XmlReader reader, TypeInfo info, Dictionary<TypeMemberInfo, object> memberValues)
      {
         if (reader == null) throw new ArgumentNullException("reader");
         if (info == null) throw new ArgumentNullException("info");
         if (memberValues == null) throw new ArgumentNullException("memberValues");
         if (!info.MemberInfoByEntityName.ContainsKey(reader.Name))
            return;
         if (info.MemberInfoByEntityName[reader.Name].IsAttribute)
            return;
         var memberInfo = info.MemberInfoByEntityName[reader.Name];
         RefManager.SetWorkingMember(memberInfo);
         int id = 0;
         bool isReferenceOrStruct = TypeHelper.IsClassOrStruct(memberInfo.DataType);
         if (isReferenceOrStruct)
         {
            var type = TypeHelper.GetReferenceType(reader);
            if (memberInfo.DataType != type)<commit_msg>Resolved type conflicts caused by rename.<commit_after>﻿#region Apache License 2.0

// Copyright 2015 <NAME>
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml;
using Fasterflect;
using Org.Edgerunner.DotSerialize.Exceptions;
using Org.Edgerunner.DotSerialize.Reflection;
using Org.Edgerunner.DotSerialize.Reflection.Construction;
using Org.Edgerunner.DotSerialize.Serialization.Factories;
using Org.Edgerunner.DotSerialize.Serialization.Reference;

namespace Org.Edgerunner.DotSerialize.Serialization
{
   public class DefaultTypeSerializer
   {
      protected ITypeSerializerFactory Factory { get; set; }
      protected IReferenceManager RefManager { get; set; }
      public ITypeInspector TypeInspector { get; set; }

      /// <summary>
      ///    Initializes a new instance of the <see cref="DefaultTypeSerializer" /> class.
      /// </summary>
      /// <param name="factory"></param>
      /// <param name="referenceManager"></param>
      /// <param name="typeInspector"></param>
      public DefaultTypeSerializer(ITypeSerializerFactory factory, IReferenceManager referenceManager, ITypeInspector typeInspector)
      {
         Factory = factory;
         RefManager = referenceManager;
         TypeInspector = typeInspector;
      }

      public virtual T Deserialize<T>(XmlReader reader)
      {
         if (reader == null) throw new ArgumentNullException("reader");
         return (T)Deserialize(reader, typeof(T));
      }

      public virtual object Deserialize(XmlReader reader, Type type)
      {
         if (reader == null) throw new ArgumentNullException("reader");
         if (type == null) throw new ArgumentNullException("type");
         object result = null;
         // Handle attributes
         if ((reader.NodeType == XmlNodeType.Attribute))
         {
            if (!((TypeHelper.IsPrimitive(type)) || (TypeHelper.IsEnum(type))))
               throw new SerializationException("Only primitives or enums can be stored in attributes.");

            if (TypeHelper.IsPrimitive(type))
               result = DeserializePrimitive(type, reader);
            else if (TypeHelper.IsEnum(type))
               result = DeserializeEnum(type, reader);
            else
               throw new SerializationException(string.Format("Unable to deserialize unexpected type \"{0}\" from an attribute.", type.Name()));
            return result;
         }
         // Handle Elements
         if (reader.NodeType == XmlNodeType.Element)
         {
            if (TypeHelper.IsPrimitive(type))
               result = DeserializePrimitive(type, reader);
            else if (TypeHelper.IsArray(type))
               result = DeserializeArray(type, reader);
            else if (TypeHelper.IsEnum(type))
               result = DeserializeEnum(type, reader);
            else
            {
               // Class or struct
               if (TypeHelper.ReferenceIsNull(reader))
                  return null;

               var memberValues = new Dictionary<TypeMemberInfo, object>();
               RefManager.StartLateBindingCapture(type);
               var info = TypeInspector.GetInfo(type);

               // read attributes
               while (reader.MoveToNextAttribute())
                  DeserializeAttribMember(reader, info, memberValues);
               reader.MoveToContent();

               if (!reader.IsEmptyElement)
                  // read child elements
                  // ReSharper disable once LoopVariableIsNeverChangedInsideLoop
                  while (ReadNextElement(reader))
                     if (reader.NodeType == XmlNodeType.Element)
                        DeserializeElementMember(reader, info, memberValues);

               result = TypeFactory.CreateInstance(type, memberValues);
               if (result == null)
                  throw new SerializationException(string.Format("Unable to create an instance of type \"{0}\".", type.Name()));

               RefManager.FinishCaptures(result);
            }

            // hand back our new object          
            return result;
         }
         throw new SerializationException(
            string.Format("Reader was not positioned on a node of type Attribute or Element.\r\n" +
                          "A custom type serializer probably positioned the reader incorrectly." +
                          "Unable to deserialize type \"{0}\".",
                          type.Name()));
      }

      protected virtual void DeserializeAttribMember(XmlReader reader,
                                                  Reflection.TypeInfo info,
                                                  Dictionary<TypeMemberInfo, object> memberValues)
      {
         if (reader == null) throw new ArgumentNullException("reader");
         if (info == null) throw new ArgumentNullException("info");
         if (memberValues == null) throw new ArgumentNullException("memberValues");
         if (!info.MemberInfoByEntityName.ContainsKey(reader.Name))
            return;
         if (!info.MemberInfoByEntityName[reader.Name].IsAttribute)
            return;
         var memberInfo = info.MemberInfoByEntityName[reader.Name];
         // Attempt to fetch a custom type serializer
         ITypeSerializer typeSerializer =
            Factory.CallMethod(new[] { memberInfo.DataType }, "GetTypeSerializer") as ITypeSerializer;
         if (typeSerializer != null)
            memberValues[memberInfo] = typeSerializer.Deserialize(reader, memberInfo.DataType);
         else
         // Since there was no bound custom type serializer we default to the GenericTypeSerializer
         {
            var defaultSerializer = Factory.GetDefaultSerializer();
            memberValues[memberInfo] = defaultSerializer.Deserialize(reader, memberInfo.DataType);
         }
      }

      protected virtual void DeserializeElementMember(XmlReader reader, Reflection.TypeInfo info, Dictionary<TypeMemberInfo, object> memberValues)
      {
         if (reader == null) throw new ArgumentNullException("reader");
         if (info == null) throw new ArgumentNullException("info");
         if (memberValues == null) throw new ArgumentNullException("memberValues");
         if (!info.MemberInfoByEntityName.ContainsKey(reader.Name))
            return;
         if (info.MemberInfoByEntityName[reader.Name].IsAttribute)
            return;
         var memberInfo = info.MemberInfoByEntityName[reader.Name];
         RefManager.SetWorkingMember(memberInfo);
         int id = 0;
         bool isReferenceOrStruct = TypeHelper.IsClassOrStruct(memberInfo.DataType);
         if (isReferenceOrStruct)
         {
            var type = TypeHelper.GetReferenceType(reader);
            if (memberInfo.DataType != type)
{'fraction_non_alphanumeric': 0.08987771739130435, 'fraction_numerical': 0.0019021739130434783, 'mean_word_length': 2.317782285327924, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6597871', 'n_tokens_mistral': 3482, 'n_tokens_neox': 3449, 'n_words': 982}