using System; using System.Collections.Generic; using System.Globalization; using System.Linq; namespace AppConv.General{ internal abstract class DecimalUnitConverterBase<T> : IUnitType where T : struct{ internal sealed class DecimalFuncMap : Dictionary<T, Func<decimal, decimal>>{ public DecimalFuncMap(){} public DecimalFuncMap(int capacity) : base(capacity){} } internal sealed class NameMap : Dictionary<string, T>{ public NameMap(){} public NameMap(int capacity) : base(capacity){} } protected abstract NameMap Names { get; } protected abstract DecimalFuncMap ConvertTo { get; } protected abstract DecimalFuncMap ConvertFrom { get; } protected virtual int Precision{ get{ return 0; } } protected virtual bool CaseCheck{ get{ return false; } } protected virtual NumberStyles NumberStyle{ get{ return NumberStyles.Float & ~NumberStyles.AllowLeadingSign; } } protected virtual string ProcessSrc(string src){ return src; } protected virtual string ProcessDst(string dst){ return dst; } protected abstract bool IsValueInvalid(T value); protected virtual decimal Convert(decimal value, T from, T to){ return ConvertFrom[to](ConvertTo[from](value)); } protected virtual string Format(decimal value){ if (Precision > 0){ decimal truncated = decimal.Truncate(value); if (value == truncated){ return truncated.ToString(CultureInfo.InvariantCulture); } } int decimalPlaces = Precision; if (Math.Abs(value) < 1M){ double fractionalPart = (double)Math.Abs(value%1M); int fractionalZeroCount = -(int)Math.Ceiling(Math.Log(fractionalPart, 10D)); decimalPlaces = Math.Min(28, fractionalZeroCount+Precision); } string result = decimal.Round(value, decimalPlaces, MidpointRounding.ToEven).ToString(CultureInfo.InvariantCulture); if (decimalPlaces > 0){ result = result.TrimEnd('0').TrimEnd('.'); } return result; } public bool TryProcess(string src, string dst, out string result){ src = ProcessSrc(src); dst = ProcessDst(dst); KeyValuePair<string, T>[] pairs = new KeyValuePair<string, T>[2]; for(int index = 0; index < 2; index++){ string str = index == 0 ? src : dst; if (CaseCheck){ List<KeyValuePair<string, T>> list = Names.Where(kvp => str.EndsWith(kvp.Key, StringComparison.InvariantCultureIgnoreCase) && (str.Length == kvp.Key.Length || !char.IsLetter(str[str.Length-kvp.Key.Length-1]))).ToList(); if (list.Count == 1){ pairs[index] = list[0]; } else{ pairs[index] = list.FirstOrDefault(kvp => str.EndsWith(kvp.Key, StringComparison.InvariantCulture)); } } else{ pairs[index] = Names.FirstOrDefault(kvp => str.EndsWith(kvp.Key, StringComparison.InvariantCultureIgnoreCase) && (str.Length == kvp.Key.Length || !char.IsLetter(str[str.Length-kvp.Key.Length-1]))); } if (IsValueInvalid(pairs[index].Value)){ result = string.Empty; return false; } if (index == 0){ src = src.Substring(0, src.Length-pairs[index].Key.Length).TrimEnd(); } } decimal value; if (decimal.TryParse(src, NumberStyle, CultureInfo.InvariantCulture, out value)){ result = Format(Convert(value, pairs[0].Value, pairs[1].Value)); return true; } else{ result = string.Empty; return false; } } } }