guibranco/CrispyWaffle

View on GitHub
Src/CrispyWaffle/Extensions/ConversionExtensions.cs

Summary

Maintainability
D
1 day
Test Coverage
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using CrispyWaffle.GoodPractices;
using CrispyWaffle.Utilities;
using Newtonsoft.Json.Linq;
using Formatting = Newtonsoft.Json.Formatting;

namespace CrispyWaffle.Extensions
{
    /// <summary>
    /// Class ConversionExtensions.
    /// </summary>
    public static class ConversionExtensions
    {
        /// <summary>
        /// An IEnumerable&lt;String&gt; extension method that converts a binary representation
        /// string to the bytes.
        /// </summary>
        /// <param name="input">The string to act on.</param>
        /// <returns>string in binary representation as a Byte[].</returns>
        public static byte[] ToBytes(this IEnumerable<string> input)
        {
            return input.Select(s => Convert.ToByte(s, 2)).ToArray();
        }

        /// <summary>
        /// A String extension method that converts this object to a boolean. This method assumes
        /// that <b>any</b> value other than stated in the optional parameter toTrue will be valid
        /// as false. It is useful for converting binary flags that can only take on two distinct
        /// values​​, or that only one value represents success and any other is invalid.
        /// </summary>
        /// <param name="str">The str to act on.</param>
        /// <param name="validValueForTrue">(Optional) the valid value for true.</param>
        /// <returns>The given data converted to a Boolean.</returns>
        public static bool ToBoolean(this string str, string validValueForTrue = "S")
        {
            return str?.Equals(validValueForTrue, StringComparison.OrdinalIgnoreCase) == true;
        }

        /// <summary>
        /// A Boolean extension method that convert this object into a String representation.
        /// </summary>
        /// <param name="boolean">The boolean to act on.</param>
        /// <param name="trueValue">The value if true.</param>
        /// <param name="falseValue">The value if false.</param>
        /// <returns>The given data converted to a String.</returns>
        public static string ToString(this bool boolean, string trueValue, string falseValue)
        {
            return boolean ? trueValue : falseValue;
        }

        /// <summary>
        /// Converts to datetime.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>DateTime.</returns>
        /// <exception cref="System.ArgumentNullException">input - Input value cannot be null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">
        /// input - Unable to parse the string to a valid datetime
        /// </exception>
        public static DateTime ToDateTime(this string input)
        {
            if (string.IsNullOrWhiteSpace(input))
            {
                throw new ArgumentNullException(nameof(input), "Input value cannot be null.");
            }

            if (input.TryToDateTime(out var result))
            {
                return result;
            }

            throw new ArgumentOutOfRangeException(
                nameof(input),
                input,
                "Unable to parse the string to a valid datetime."
            );
        }

        /// <summary>
        /// Tries to convert string to date time.
        /// </summary>
        /// <param name="input">The input string a valid DateTime format.</param>
        /// <param name="value">The DateTime value.</param>
        /// <returns><b>True</b> if success, <b>false</b> otherwise.</returns>
        public static bool TryToDateTime(this string input, out DateTime value)
        {
            value = DateTime.MinValue;
            if (string.IsNullOrWhiteSpace(input))
            {
                return false;
            }

            input = input.ToLowerInvariant().Trim();
            switch (input)
            {
                case "now":

                    value = DateTime.Now;

                    return true;

                case "today":

                    value = DateTime.Today;

                    return true;

                case "yesterday":

                    value = DateTime.Today.AddDays(-1);

                    return true;

                case "tomorrow":

                    value = DateTime.Today.AddDays(1);

                    return true;

                default:

                    if (DateTime.TryParse(input, out value))
                    {
                        return true;
                    }

                    return input.Length == 10
                        && DateTime.TryParseExact(
                            input,
                            @"dd/MM/yyyy",
                            CultureInfo.InvariantCulture,
                            DateTimeStyles.None,
                            out value
                        );
            }
        }

        /// <summary>
        /// Converts to int32.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>System.Int32.</returns>
        public static int ToInt32(this string input)
        {
            if (string.IsNullOrWhiteSpace(input))
            {
                return 0;
            }

            var success = int.TryParse(
                input,
                NumberStyles.Number,
                CultureInfo.CurrentCulture,
                out var result
            );

            return success ? result : 0;
        }

        /// <summary>
        /// To the int64.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>Int64.</returns>
        public static long ToInt64(this string input)
        {
            if (string.IsNullOrWhiteSpace(input))
            {
                return 0;
            }

            var success = long.TryParse(
                input,
                NumberStyles.Number,
                CultureInfo.CurrentCulture,
                out var result
            );

            return success ? result : 0;
        }

        /// <summary>
        /// A String extension method that converts a string to a decimal.
        /// </summary>
        /// <param name="input">The str to act on.</param>
        /// <returns>str as a Decimal.</returns>
        public static decimal ToDecimal(this string input)
        {
            if (string.IsNullOrWhiteSpace(input))
            {
                return 0M;
            }

            return decimal.TryParse(
                input,
                NumberStyles.Number,
                CultureInfo.CurrentCulture,
                out var result
            )
                ? result
                : 0M;
        }

        /// <summary>
        /// An IEnumerable&lt;Byte&gt; extension method that converts the bytes to a binary string
        /// representation string.
        /// </summary>
        /// <param name="bytes">The bytes to act on.</param>
        /// <returns>bytes as a Binary String[] representation.</returns>
        public static string[] ToBinaryString(this IEnumerable<byte> bytes)
        {
            return bytes.Select(b => Convert.ToString(b, 2).PadLeft(8, '0')).ToArray();
        }

        /// <summary>
        /// Converts to monetary.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <param name="cultureInfo">The culture information.</param>
        /// <returns>System.String.</returns>
        public static string ToMonetary(this decimal input, CultureInfo cultureInfo = null)
        {
            return input == 0
                ? "No value"
                : string.Format(cultureInfo ?? CultureInfo.CurrentCulture, "{0:C}", input);
        }

        /// <summary>
        /// Converts a DateTime instance to Unix Timestamp (number of seconds that have elapsed
        /// since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970).
        /// </summary>
        /// <param name="dateTime">The date time.</param>
        /// <returns>System.Int32.</returns>
        public static int ToUnixTimeStamp(this DateTime dateTime) =>
            (int)
                dateTime
                    .ToUniversalTime()
                    .Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified))
                    .TotalSeconds;

        /// <summary>
        /// Converts a Unix Timestamp (number of seconds that have elapsed since 00:00:00
        /// Coordinated Universal Time (UTC), Thursday, 1 January 1970) to a DateTime instance.
        /// </summary>
        /// <param name="epochTime">The Unix Timestamp.</param>
        /// <returns>A DateTime instance of the epochTime.</returns>
        public static DateTime FromUnixTimeStamp(this int epochTime)
        {
            return new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)
                .AddSeconds(Math.Round((double)epochTime / 1000))
                .ToLocalTime();
        }

        /// <summary>
        /// Parses the brazilian phone number.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <returns>PhoneNumber.</returns>
        /// <exception cref="InvalidTelephoneNumberException">
        /// The value '{telephoneNumber}' isn't a valid telephone number.
        /// </exception>
        public static PhoneNumber ParseBrazilianPhoneNumber(this string number)
        {
            var result = new PhoneNumber(0, 0, 0);

            if (number.TryParseBrazilianPhoneNumber(ref result))
            {
                return result;
            }

            throw new InvalidTelephoneNumberException(number.RemoveNonNumeric());
        }

        /// <summary>
        /// Tries the parse brazilian phone number.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <param name="result">The result.</param>
        /// <returns><c>true</c> if is valid phone number, <c>false</c> otherwise.</returns>
        public static bool TryParseBrazilianPhoneNumber(this string number, ref PhoneNumber result)
        {
            var dirty = number.RemoveNonNumeric();
            var dirtyLength = dirty.Length;

            if (
                dirty.StartsWith("55", StringComparison.OrdinalIgnoreCase)
                && dirtyLength is > 11 and < 15
            )
            {
                dirty = dirty.Remove(0, 2);
                dirtyLength -= 2;
            }

            if (dirtyLength < 10 || dirtyLength > 12)
            {
                return false;
            }

            var prefix =
                dirty.Substring(0, 1).Equals(@"0", StringComparison.OrdinalIgnoreCase)
                && (dirtyLength == 11 || dirtyLength == 12)
                    ? dirty.Substring(1, 2)
                    : dirty.Substring(0, 2);

            var hasNineDigits = dirty
                .Substring(dirtyLength - 9, 1)
                .Equals(@"9", StringComparison.OrdinalIgnoreCase);

            var allowedDigits = hasNineDigits ? 9 : 8;

            var telephoneNumber = dirty.Substring(dirtyLength - allowedDigits, allowedDigits);

            result = new PhoneNumber(55, prefix.ToInt32(), telephoneNumber.ToInt64());

            return true;
        }

        /// <summary>
        /// To the pretty string.
        /// </summary>
        /// <param name="json">The json.</param>
        /// <returns>System.String.</returns>
        public static string ToPrettyString(this string json)
        {
            if (string.IsNullOrWhiteSpace(json))
            {
                return string.Empty;
            }

            if (!json.IsValidJson())
            {
                return json;
            }

            var parsedJson = JToken.Parse(json);

            return parsedJson.ToString(Formatting.Indented);
        }

        /// <summary>
        /// To the ident string.
        /// </summary>
        /// <param name="document">The document.</param>
        /// <returns>System.String.</returns>
        public static string ToIdentString(this XmlDocument document)
        {
            if (document == null)
            {
                return null;
            }

            var builder = new StringBuilder();

            var settings = new XmlWriterSettings
            {
                Indent = true,
                IndentChars = "\t",
                NewLineChars = "\r\n",
                NewLineHandling = NewLineHandling.Replace
            };

            using (var writer = XmlWriter.Create(builder, settings))
            {
                document.Save(writer);
            }

            return builder.ToString();
        }

        /// <summary>
        /// Calculates the module-10 of a string.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns>System.Int32.</returns>
        public static int ToModule10(this string input)
        {
            var number = Regex.Replace(
                input,
                "[^0-9]",
                string.Empty,
                RegexOptions.Compiled,
                TimeSpan.FromSeconds(0.5)
            );

            var sum = 0;

            var weight = 2;

            var counter = number.Length - 1;

            while (counter >= 0)
            {
                var multiplication = number.Substring(counter, 1).ToInt32() * weight;

                if (multiplication >= 10)
                {
                    multiplication = 1 + (multiplication - 10);
                }

                sum += multiplication;

                weight = weight == 2 ? 1 : 2;

                counter--;
            }

            var digit = 10 - (sum % 10);

            if (digit == 10)
            {
                digit = 0;
            }

            return digit;
        }

        /// <summary>
        /// The ordinal suffix.
        /// </summary>
        private static readonly Dictionary<int, string> _ordinalSuffix = new Dictionary<int, string>
        {
            { 1, "st" },
            { 2, "nd" },
            { 3, "rd" }
        };

        /// <summary>
        /// To the ordinal.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <returns>System.String.</returns>
        public static string ToOrdinal(this long number)
        {
            if (number < 0)
            {
                return number.ToString(CultureInfo.CurrentCulture);
            }

            var rem = number % 100;

            if (rem >= 11 && rem <= 13)
            {
                return $"{number}th";
            }

            var key = (int)number % 10;
            if (_ordinalSuffix.TryGetValue(key, out var value))
            {
                return $"{number}{value}";
            }

            return $"{number}th";
        }

        /// <summary>
        /// To the ordinal.
        /// </summary>
        /// <param name="number">The number.</param>
        /// <returns>System.String.</returns>
        public static string ToOrdinal(this int number)
        {
            return ((long)number).ToOrdinal();
        }

        /// <summary>
        /// Formats the document.
        /// </summary>
        /// <param name="document">The document.</param>
        /// <returns>System.String.</returns>
        public static string FormatBrazilianDocument(this string document)
        {
            if (string.IsNullOrWhiteSpace(document))
            {
                return "Invalid document";
            }

            var documentPattern =
                document.Length == 14 ? @"{0:00\.000\.000/0000-00}" : @"{0:000\.000\.000-00}";
            return string.Format(documentPattern, document.RemoveNonNumeric().ToInt64());
        }

        /// <summary>
        /// Formats the zip code.
        /// </summary>
        /// <param name="zipCode">The zip code.</param>
        /// <returns>System.String.</returns>
        public static string FormatBrazilianZipCode(this string zipCode)
        {
            if (string.IsNullOrWhiteSpace(zipCode))
            {
                return "Invalid zipcode";
            }

            return Regex.Replace(
                zipCode.RemoveNonNumeric(),
                @"(\d{5})(\d{3})",
                "$1-$2",
                RegexOptions.Compiled,
                TimeSpan.FromSeconds(10)
            );
        }

        /// <summary>
        /// Deeps the clone.
        /// </summary>
        /// <typeparam name="T">The type parameter.</typeparam>
        /// <param name="instance">The instance.</param>
        /// <param name="useNonPublic">if set to <c>true</c> [use non public].</param>
        /// <returns>T.</returns>
        public static T DeepClone<T>(this T instance, bool useNonPublic = true)
        {
            var type = typeof(T);

            var constructors = type.GetConstructors()
                .OrderByDescending(c => c.GetParameters().Length);
            var ctor = constructors.FirstOrDefault();

            if (ctor == null)
            {
                return default;
            }

            var arguments = new List<object>();
            var parameters = ctor.GetParameters();

            foreach (var parameter in parameters)
            {
                ParseParameters(instance, useNonPublic, type, parameter, arguments);
            }

            return (T)ctor.Invoke(arguments.ToArray());
        }

        /// <summary>
        /// Parses the parameters.
        /// </summary>
        /// <typeparam name="T">The type parameter.</typeparam>
        /// <param name="instance">The instance.</param>
        /// <param name="useNonPublic">if set to <c>true</c> [use non public].</param>
        /// <param name="type">The type.</param>
        /// <param name="parameter">The parameter.</param>
        /// <param name="arguments">The arguments.</param>
        private static void ParseParameters<T>(
            T instance,
            bool useNonPublic,
            Type type,
            ParameterInfo parameter,
            List<object> arguments
        )
        {
            var property = type.GetProperty(
                parameter.Name,
                BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance
            );

            if (property == null && !useNonPublic)
            {
                return;
            }

            if (property == null)
            {
                property = type.GetProperty(
                    parameter.Name,
                    BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.Instance
                );

                if (property == null)
                {
                    return;
                }
            }

            if (property.PropertyType != parameter.ParameterType)
            {
                return;
            }

            arguments.Add(property.GetValue(instance));
        }
    }
}