<commit_before>﻿using RestSharp;
using RestSharp.Deserializers;
using RestSharp.Extensions;
using RestSharp.Serializers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;

namespace Harvest.Net.Serialization
{
    public class HarvestXmlDeserializer : IDeserializer
    {
        public string RootElement { get; set; }

        public string Namespace { get; set; }

        public string DateFormat { get; set; }

        public CultureInfo Culture { get; set; }

        public HarvestXmlDeserializer()
        {
            Culture = CultureInfo.InvariantCulture;
        }

        public virtual T Deserialize<T>(IRestResponse response)
        {
            if (string.IsNullOrWhiteSpace(response.Content))
                return default(T);

            var doc = XDocument.Parse(response.Content);
            var root = doc.Root;
            if (RootElement.HasValue() && doc.Root != null)
            {
                root = doc.Root.Element(RootElement.AsNamespaced(Namespace));
            }

            // autodetect xml namespace
            if (!Namespace.HasValue())
            {
                RemoveNamespace(doc);
            }

            var x = Activator.CreateInstance<T>();
            var objType = x.GetType();

            if (objType.IsSubclassOfRawGeneric(typeof(List<>)))
            {
                x = (T)HandleListDerivative(x, root, objType.Name, objType);
            }
            else
            {
                Map(x, root);
            }

            return x;
        }

        private void RemoveNamespace(XDocument xdoc)
        {
            foreach (XElement e in xdoc.Root.DescendantsAndSelf())
            {
                if (e.Name.Namespace != XNamespace.None)
                {
                    e.Name = XNamespace.None.GetName(e.Name.LocalName);
                }

                if (e.Attributes().Any(a => a.IsNamespaceDeclaration || a.Name.Namespace != XNamespace.None))
                {
                    e.ReplaceAttributes(e.Attributes().Select(a => a.IsNamespaceDeclaration ? null : a.Name.Namespace != XNamespace.None ? new XAttribute(XNamespace.None.GetName(a.Name.LocalName), a.Value) : a));
                }
            }
        }

        protected virtual void Map(object x, XElement root)
        {
            var objType = x.GetType();
            var props = objType.GetProperties();

            foreach (var prop in props)
            {
                var type = prop.PropertyType;

                if (!type.IsPublic || !prop.CanWrite)
                    continue;

                var name = prop.Name.AsNamespaced(Namespace);
                var value = GetValueFromXml(root, name, prop);

                if (value == null)
                {
                    // special case for inline list items
                    if (type.IsGenericType)
                    {
                        var genericType = type.GetGenericArguments()[0];
                        var first = GetElementByName(root, genericType.Name);
                        var list = (IList)Activator.CreateInstance(type);

                        if (first != null)
                        {
                            var elements = root.Elements(first.Name);
                            PopulateListFromElements(genericType, elements, list);
                        }

                        prop.SetValue(x, list, null);
                    }

                    continue;
                }

                // check for nullable and extract underlying type
                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    // if the value is empty, set the property to null...
                    if (value == null || string.IsNullOrEmpty(value.ToString()))
                    {
                        prop.SetValue(x, null, null);
                        continue;
                    }

                    type = type.GetGenericArguments()[0];
                }

                if (type == typeof(bool))
                {
                    var toConvert = value.ToString().ToLower();
                    prop.SetValue(x, XmlConvert.ToBoolean(toConvert), null);
                }
                else if (type.IsPrimitive)
                {
                    prop.SetValue(x, value.ChangeType(type, Culture), null);
                }
                else if (type.IsEnum)
                {
                    var match = Enum.GetValues(type)
                        .Cast<Enum>()
                        .FirstOrDefault(v =>
                        {
                            var attr = type.GetMember(v.ToString())[0].GetAttribute<DescriptionAttribute>();
                            if (attr != null && attr.Description == value.ToString())<commit_msg>Use abbreviation matching for currency<commit_after>﻿using RestSharp;
using RestSharp.Deserializers;
using RestSharp.Extensions;
using RestSharp.Serializers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;

namespace Harvest.Net.Serialization
{
    public class HarvestXmlDeserializer : IDeserializer
    {
        public string RootElement { get; set; }

        public string Namespace { get; set; }

        public string DateFormat { get; set; }

        public CultureInfo Culture { get; set; }

        public HarvestXmlDeserializer()
        {
            Culture = CultureInfo.InvariantCulture;
        }

        public virtual T Deserialize<T>(IRestResponse response)
        {
            if (string.IsNullOrWhiteSpace(response.Content))
                return default(T);

            var doc = XDocument.Parse(response.Content);
            var root = doc.Root;
            if (RootElement.HasValue() && doc.Root != null)
            {
                root = doc.Root.Element(RootElement.AsNamespaced(Namespace));
            }

            // autodetect xml namespace
            if (!Namespace.HasValue())
            {
                RemoveNamespace(doc);
            }

            var x = Activator.CreateInstance<T>();
            var objType = x.GetType();

            if (objType.IsSubclassOfRawGeneric(typeof(List<>)))
            {
                x = (T)HandleListDerivative(x, root, objType.Name, objType);
            }
            else
            {
                Map(x, root);
            }

            return x;
        }

        private void RemoveNamespace(XDocument xdoc)
        {
            foreach (XElement e in xdoc.Root.DescendantsAndSelf())
            {
                if (e.Name.Namespace != XNamespace.None)
                {
                    e.Name = XNamespace.None.GetName(e.Name.LocalName);
                }

                if (e.Attributes().Any(a => a.IsNamespaceDeclaration || a.Name.Namespace != XNamespace.None))
                {
                    e.ReplaceAttributes(e.Attributes().Select(a => a.IsNamespaceDeclaration ? null : a.Name.Namespace != XNamespace.None ? new XAttribute(XNamespace.None.GetName(a.Name.LocalName), a.Value) : a));
                }
            }
        }

        protected virtual void Map(object x, XElement root)
        {
            var objType = x.GetType();
            var props = objType.GetProperties();

            foreach (var prop in props)
            {
                var type = prop.PropertyType;

                if (!type.IsPublic || !prop.CanWrite)
                    continue;

                var name = prop.Name.AsNamespaced(Namespace);
                var value = GetValueFromXml(root, name, prop);

                if (value == null)
                {
                    // special case for inline list items
                    if (type.IsGenericType)
                    {
                        var genericType = type.GetGenericArguments()[0];
                        var first = GetElementByName(root, genericType.Name);
                        var list = (IList)Activator.CreateInstance(type);

                        if (first != null)
                        {
                            var elements = root.Elements(first.Name);
                            PopulateListFromElements(genericType, elements, list);
                        }

                        prop.SetValue(x, list, null);
                    }

                    continue;
                }

                // check for nullable and extract underlying type
                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    // if the value is empty, set the property to null...
                    if (value == null || string.IsNullOrEmpty(value.ToString()))
                    {
                        prop.SetValue(x, null, null);
                        continue;
                    }

                    type = type.GetGenericArguments()[0];
                }

                if (type == typeof(bool))
                {
                    var toConvert = value.ToString().ToLower();
                    prop.SetValue(x, XmlConvert.ToBoolean(toConvert), null);
                }
                else if (type.IsPrimitive)
                {
                    prop.SetValue(x, value.ChangeType(type, Culture), null);
                }
                else if (type.IsEnum)
                {
                    var match = Enum.GetValues(type)
                        .Cast<Enum>()
                        .FirstOrDefault(v =>
                        {
                            var attr = type.GetMember(v.ToString())[0].GetAttribute<DescriptionAttribute>();
                            if (attr != null && attr.Description == value.ToString())
                                return true;
                            else if (v.ToString() == value.ToString().Split('-').Reverse().First().Trim()) // for currency, match only the abbreviation part
{'fraction_non_alphanumeric': 0.09584509097985794, 'fraction_numerical': 0.0005838279653595408, 'mean_word_length': 1.3258655804480652, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4258224', 'n_tokens_mistral': 2559, 'n_tokens_neox': 2387, 'n_words': 542}