1
0
mirror of https://github.com/chylex/Query.git synced 2025-04-29 11:34:06 +02:00
Query/AppConv/General/DecimalUnitConverterBase.cs
2024-08-05 20:42:15 +02:00

113 lines
3.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace AppConv.General;
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 => 0;
protected virtual bool CaseCheck => false;
protected virtual NumberStyles NumberStyle => 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);
var 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[..^pairs[index].Key.Length].TrimEnd();
}
}
if (decimal.TryParse(src, NumberStyle, CultureInfo.InvariantCulture, out decimal value)) {
result = Format(Convert(value, pairs[0].Value, pairs[1].Value));
return true;
}
else {
result = string.Empty;
return false;
}
}
}