<commit_before>﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Atata.TermFormatting;

namespace Atata
{
    public static class TermResolver
    {
        private const TermCase DefaultCase = TermCase.Title;

        private const TermMatch DefaultMatch = TermMatch.Equals;

        private static readonly Dictionary<Type, TermConverter> TypeTermConverters = new Dictionary<Type, TermConverter>();

        static TermResolver()
        {
            RegisterStandardConverters();
        }

        private static void RegisterStandardConverters()
        {
            RegisterNumberConverter(sbyte.Parse);
            RegisterNumberConverter(byte.Parse);
            RegisterNumberConverter(short.Parse);
            RegisterNumberConverter(ushort.Parse);
            RegisterNumberConverter(int.Parse);
            RegisterNumberConverter(uint.Parse);
            RegisterNumberConverter(long.Parse);
            RegisterNumberConverter(ulong.Parse);
            RegisterNumberConverter(float.Parse);
            RegisterNumberConverter(double.Parse);
            RegisterNumberConverter(decimal.Parse);

            RegisterConverter<DateTime>(
                (s, opt) =>
                {
                    string stringValue = RetrieveValueFromString(s, opt.Format);
                    string specificFormat = RetrieveSpecificFormatFromStringFormat(opt.Format);

                    return specificFormat == null
                        ? DateTime.Parse(stringValue, opt.Culture)
                        : DateTime.ParseExact(stringValue, specificFormat, opt.Culture);
                });

            RegisterConverter<TimeSpan>(
                (s, opt) =>
                {
                    string stringValue = RetrieveValueFromString(s, opt.Format);
                    string specificFormat = RetrieveSpecificFormatFromStringFormat(opt.Format);

                    if (specificFormat == null)
                        return TimeSpan.Parse(stringValue, opt.Culture);
                    else if (specificFormat.Contains("t"))
                        return DateTime.ParseExact(stringValue, specificFormat, opt.Culture).TimeOfDay;
                    else
                        return TimeSpan.ParseExact(stringValue, specificFormat, opt.Culture);
                },
                (v, opt) =>
                {
                    string specificFormat = RetrieveSpecificFormatFromStringFormat(opt.Format);

                    return specificFormat != null && specificFormat.Contains("t")
                        ? FormatValue(
                            DateTime.Today.Add(v).ToString(specificFormat, opt.Culture),
                            opt.Format,
                            opt.Culture)
                        : FormatValue(v, opt.Format, opt.Culture);
                });

            RegisterConverter<Guid>(
                (s, opt) =>
                {
                    string stringValue = RetrieveValueFromString(s, opt.Format);
                    string specificFormat = RetrieveSpecificFormatFromStringFormat(opt.Format);

                    return specificFormat == null
                        ? Guid.Parse(stringValue)
                        : Guid.ParseExact(stringValue, specificFormat);
                });
        }

        private static void RegisterNumberConverter<T>(
            Func<string, NumberStyles, IFormatProvider, T> parseFunction)
            where T : IFormattable
        {
            RegisterConverter(
                typeof(T),
                (s, opt) =>
                {
                    string stringValue = RetrieveValueFromString(s, opt.Format);
                    string specificFormat = RetrieveSpecificFormatFromStringFormat(opt.Format);

                    bool isPercentageFormat = specificFormat != null && specificFormat.StartsWith("P", StringComparison.InvariantCultureIgnoreCase);

                    if (isPercentageFormat)
                    {
                        stringValue = stringValue.
                            Replace(opt.Culture.NumberFormat.PercentSymbol, string.Empty).
                            Replace(opt.Culture.NumberFormat.PercentDecimalSeparator, opt.Culture.NumberFormat.NumberDecimalSeparator);

                        decimal percent = decimal.Parse(stringValue, NumberStyles.Any, opt.Culture) / 100;
                        return Convert.ChangeType(percent, typeof(T), opt.Culture);
                    }
                    else
                    {
                        return parseFunction(stringValue, NumberStyles.Any, opt.Culture);
                    }
                });
        }

        public static void RegisterConverter<T>(
            Func<string, TermOptions, T> fromStringConverter,
            Func<T, TermOptions, string> toStringConverter = null)
        {
            fromStringConverter.CheckNotNull(nameof(fromStringConverter));

            Func<string, TermOptions, object> castedFromStringConverter = (s, to) => fromStringConverter(s, to);
            Func<object, TermOptions, string> castedToStringConverter = null;
            if (toStringConverter != null)
                castedToStringConverter = (v, to) => toStringConverter((T)v, to);

            RegisterConverter(typeof(T), castedFromStringConverter, castedToStringConverter);
        }

        public static void RegisterConverter(
            Type type,
            Func<string, TermOptions, object> fromStringConverter,
            Func<object, TermOptions, string> toStringConverter = null)
        {
            fromStringConverter.CheckNotNull(nameof(fromStringConverter));

            TypeTermConverters[type] = new TermConverter
            {
                FromStringConverter = fromStringConverter,
                ToStringConverter = toStringConverter
            };
        }

        public static string ToDisplayString(object value, TermOptions termOptions = null)
        {
            return value is IEnumerable<object>
                ? string.Join("/", ((IEnumerable<object>)value).Select(x => ToDisplayString(x, termOptions)))
                : ToString(value, termOptions);
        }

        public static string ToString(object value, TermOptions termOptions = null)
        {
            if (value == null || Equals(value, string.Empty))
                return null;

            string[] terms = GetTerms(value, termOptions);
            return string.Join("/", terms);
        }

        public static string[] GetTerms(object value, TermOptions termOptions = null)
        {
            value.CheckNotNull(nameof(value));

            termOptions = termOptions ?? new TermOptions();
            TermConverter termConverter;

            if (value is string stringValue)
                return new[] { FormatStringValue(stringValue, termOptions) };
            else if (value is Enum enumValue)
                return GetEnumTerms(enumValue, termOptions);
            else if (TypeTermConverters.TryGetValue(value.GetType(), out termConverter) && termConverter.ToStringConverter != null)
                return new[] { termConverter.ToStringConverter(value, termOptions) };
            else
                return new[] { FormatValue(value, termOptions.Format, termOptions.Culture) };
        }

        private static string FormatStringValue(string value, TermOptions termOptions)
        {<commit_msg>Update TermResolver.ToString method to return string.Empty when value argument equals string.Empty<commit_after>﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Atata.TermFormatting;

namespace Atata
{
    public static class TermResolver
    {
        private const TermCase DefaultCase = TermCase.Title;

        private const TermMatch DefaultMatch = TermMatch.Equals;

        private static readonly Dictionary<Type, TermConverter> TypeTermConverters = new Dictionary<Type, TermConverter>();

        static TermResolver()
        {
            RegisterStandardConverters();
        }

        private static void RegisterStandardConverters()
        {
            RegisterNumberConverter(sbyte.Parse);
            RegisterNumberConverter(byte.Parse);
            RegisterNumberConverter(short.Parse);
            RegisterNumberConverter(ushort.Parse);
            RegisterNumberConverter(int.Parse);
            RegisterNumberConverter(uint.Parse);
            RegisterNumberConverter(long.Parse);
            RegisterNumberConverter(ulong.Parse);
            RegisterNumberConverter(float.Parse);
            RegisterNumberConverter(double.Parse);
            RegisterNumberConverter(decimal.Parse);

            RegisterConverter<DateTime>(
                (s, opt) =>
                {
                    string stringValue = RetrieveValueFromString(s, opt.Format);
                    string specificFormat = RetrieveSpecificFormatFromStringFormat(opt.Format);

                    return specificFormat == null
                        ? DateTime.Parse(stringValue, opt.Culture)
                        : DateTime.ParseExact(stringValue, specificFormat, opt.Culture);
                });

            RegisterConverter<TimeSpan>(
                (s, opt) =>
                {
                    string stringValue = RetrieveValueFromString(s, opt.Format);
                    string specificFormat = RetrieveSpecificFormatFromStringFormat(opt.Format);

                    if (specificFormat == null)
                        return TimeSpan.Parse(stringValue, opt.Culture);
                    else if (specificFormat.Contains("t"))
                        return DateTime.ParseExact(stringValue, specificFormat, opt.Culture).TimeOfDay;
                    else
                        return TimeSpan.ParseExact(stringValue, specificFormat, opt.Culture);
                },
                (v, opt) =>
                {
                    string specificFormat = RetrieveSpecificFormatFromStringFormat(opt.Format);

                    return specificFormat != null && specificFormat.Contains("t")
                        ? FormatValue(
                            DateTime.Today.Add(v).ToString(specificFormat, opt.Culture),
                            opt.Format,
                            opt.Culture)
                        : FormatValue(v, opt.Format, opt.Culture);
                });

            RegisterConverter<Guid>(
                (s, opt) =>
                {
                    string stringValue = RetrieveValueFromString(s, opt.Format);
                    string specificFormat = RetrieveSpecificFormatFromStringFormat(opt.Format);

                    return specificFormat == null
                        ? Guid.Parse(stringValue)
                        : Guid.ParseExact(stringValue, specificFormat);
                });
        }

        private static void RegisterNumberConverter<T>(
            Func<string, NumberStyles, IFormatProvider, T> parseFunction)
            where T : IFormattable
        {
            RegisterConverter(
                typeof(T),
                (s, opt) =>
                {
                    string stringValue = RetrieveValueFromString(s, opt.Format);
                    string specificFormat = RetrieveSpecificFormatFromStringFormat(opt.Format);

                    bool isPercentageFormat = specificFormat != null && specificFormat.StartsWith("P", StringComparison.InvariantCultureIgnoreCase);

                    if (isPercentageFormat)
                    {
                        stringValue = stringValue.
                            Replace(opt.Culture.NumberFormat.PercentSymbol, string.Empty).
                            Replace(opt.Culture.NumberFormat.PercentDecimalSeparator, opt.Culture.NumberFormat.NumberDecimalSeparator);

                        decimal percent = decimal.Parse(stringValue, NumberStyles.Any, opt.Culture) / 100;
                        return Convert.ChangeType(percent, typeof(T), opt.Culture);
                    }
                    else
                    {
                        return parseFunction(stringValue, NumberStyles.Any, opt.Culture);
                    }
                });
        }

        public static void RegisterConverter<T>(
            Func<string, TermOptions, T> fromStringConverter,
            Func<T, TermOptions, string> toStringConverter = null)
        {
            fromStringConverter.CheckNotNull(nameof(fromStringConverter));

            Func<string, TermOptions, object> castedFromStringConverter = (s, to) => fromStringConverter(s, to);
            Func<object, TermOptions, string> castedToStringConverter = null;
            if (toStringConverter != null)
                castedToStringConverter = (v, to) => toStringConverter((T)v, to);

            RegisterConverter(typeof(T), castedFromStringConverter, castedToStringConverter);
        }

        public static void RegisterConverter(
            Type type,
            Func<string, TermOptions, object> fromStringConverter,
            Func<object, TermOptions, string> toStringConverter = null)
        {
            fromStringConverter.CheckNotNull(nameof(fromStringConverter));

            TypeTermConverters[type] = new TermConverter
            {
                FromStringConverter = fromStringConverter,
                ToStringConverter = toStringConverter
            };
        }

        public static string ToDisplayString(object value, TermOptions termOptions = null)
        {
            return value is IEnumerable<object>
                ? string.Join("/", ((IEnumerable<object>)value).Select(x => ToDisplayString(x, termOptions)))
                : ToString(value, termOptions);
        }

        public static string ToString(object value, TermOptions termOptions = null)
        {
            if (value == null || Equals(value, string.Empty))
                return value as string;

            string[] terms = GetTerms(value, termOptions);
            return string.Join("/", terms);
        }

        public static string[] GetTerms(object value, TermOptions termOptions = null)
        {
            value.CheckNotNull(nameof(value));

            termOptions = termOptions ?? new TermOptions();
            TermConverter termConverter;

            if (value is string stringValue)
                return new[] { FormatStringValue(stringValue, termOptions) };
            else if (value is Enum enumValue)
                return GetEnumTerms(enumValue, termOptions);
            else if (TypeTermConverters.TryGetValue(value.GetType(), out termConverter) && termConverter.ToStringConverter != null)
                return new[] { termConverter.ToStringConverter(value, termOptions) };
            else
                return new[] { FormatValue(value, termOptions.Format, termOptions.Culture) };
        }

        private static string FormatStringValue(string value, TermOptions termOptions)
        {
{'fraction_non_alphanumeric': 0.08058485779142743, 'fraction_numerical': 0.0004005875283749499, 'mean_word_length': 1.846636259977195, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, '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': '1187198', 'n_tokens_mistral': 3416, 'n_tokens_neox': 3197, 'n_words': 700}