[WIP] Refactor default converters

pull/2/head
Matija Koželj 2 years ago
parent 82594cff34
commit 2daf5b6c1e

@ -11,8 +11,16 @@ namespace Connected
public BoolConverter()
{
SetFunc = OnSet;
GetFunc = OnGet;
}
protected override bool? ConvertValue(T? value)
{
return OnSet(value);
}
protected override T? ConvertValueBack(bool? value)
{
return OnGet(value);
}
private T OnGet(bool? value)
@ -31,12 +39,12 @@ namespace Connected
return (T)(object)(value == true ? 1 : (value == false ? (int?)0 : null));
else
{
UpdateGetError($"Conversion to type {typeof(T)} not implemented");
TriggerError($"Conversion to type {typeof(T)} not implemented");
}
}
catch (Exception e)
{
UpdateGetError("Conversion error: " + e.Message);
TriggerError("Conversion error: " + e.Message);
return default(T);
}
return default(T);
@ -69,13 +77,13 @@ namespace Connected
}
else
{
UpdateSetError("Unable to convert to bool? from type " + typeof(T).Name);
TriggerError("Unable to convert to bool? from type " + typeof(T).Name);
return null;
}
}
catch (FormatException e)
{
UpdateSetError("Conversion error: " + e.Message);
TriggerError("Conversion error: " + e.Message);
return null;
}
}

@ -1,72 +1,97 @@
using System;
using System.Globalization;
using System.Globalization;
namespace Connected
{
public class Converter<T, U>
namespace Connected;
public class Converter<TSourceType, TDestinationType>
{
public Func<T, U> SetFunc { get; set; }
public Func<U, T> GetFunc { get; set; }
public event EventHandler<string?>? ErrorOccured;
/// <summary>
/// The culture info being used for decimal points, date and time format, etc.
/// </summary>
public CultureInfo Culture { get; set; } = Converters.DefaultCulture;
public Action<string> OnError { get; set; }
public bool SetError { get; set; }
public bool GetError { get; set; }
public string SetErrorMessage { get; set; }
public string GetErrorMessage { get; set; }
public U Set(T value)
public TDestinationType? Convert(TSourceType value)
{
SetError = false;
SetErrorMessage = null;
if (SetFunc == null)
return default(U);
try
{
return SetFunc(value);
return ConvertValue(value);
}
catch (Exception e)
{
SetError = true;
SetErrorMessage = $"Conversion from {typeof(T).Name} to {typeof(U).Name} failed: {e.Message}";
TriggerError($"Conversion from {typeof(TSourceType).Name} to {typeof(TDestinationType).Name} failed: {e.Message}", e);
}
return default;
}
return default(U);
protected virtual TDestinationType? ConvertValue(TSourceType? value)
{
return default;
}
public T Get(U value)
public TSourceType? ConvertBack(TDestinationType value)
{
GetError = false;
GetErrorMessage = null;
if (GetFunc == null)
return default(T);
try
{
return GetFunc(value);
return ConvertValueBack(value);
}
catch (Exception e)
{
GetError = true;
GetErrorMessage = $"Conversion from {typeof(U).Name} to {typeof(T).Name} failed: {e.Message}";
TriggerError($"Conversion from {typeof(TDestinationType).Name} to {typeof(TSourceType).Name} failed: {e.Message}", e);
}
return default;
}
return default(T);
protected virtual TSourceType? ConvertValueBack(TDestinationType? value)
{
return default;
}
protected void UpdateSetError(string msg)
protected void TriggerError(string? msg, Exception? e = null)
{
SetError = true;
SetErrorMessage = msg;
OnError?.Invoke(msg);
ErrorOccured?.Invoke(this, msg);
OnErrorOccured(msg, e);
}
protected void UpdateGetError(string msg)
protected virtual void OnErrorOccured(string? msg, Exception? e)
{
GetError = true;
GetErrorMessage = msg;
OnError?.Invoke(msg);
}
}
/// <summary>
/// Converts values using the supplied lambda functions.
/// </summary>
/// <typeparam name="TSourceType">The source type</typeparam>
/// <typeparam name="TDestinationType">The destination type</typeparam>
public class LambdaConverter<TSourceType, TDestinationType> :Converter<TSourceType, TDestinationType>
{
private readonly Func<TSourceType, TDestinationType?>? _convertFunction;
private readonly Func<TDestinationType, TSourceType?>? _convertBackFunction;
public LambdaConverter(Func<TSourceType, TDestinationType?>? convertFunction = null, Func<TDestinationType, TSourceType?>? convertBackFunction = null)
{
_convertFunction = convertFunction;
_convertBackFunction = convertBackFunction;
}
protected override TDestinationType? ConvertValue(TSourceType? value)
{
if (_convertFunction is null)
return base.ConvertValue(value);
return _convertFunction.Invoke(value);
}
protected override TSourceType? ConvertValueBack(TDestinationType? value)
{
if (_convertFunction is null)
return base.ConvertValueBack(value);
return _convertBackFunction.Invoke(value);
}
}
@ -76,13 +101,11 @@ namespace Connected
/// Set converts to string
/// Get converts from string
/// </summary>
public class Converter<T> : Converter<T, string>
public class ToStringConverter<T> : Converter<T, string>
{
/// <summary>
/// Custom Format to be applied on bidirectional way.
/// </summary>
public string Format { get; set; } = null;
}
public string? Format { get; set; } = null;
}

@ -1,4 +1,5 @@
using System.Globalization;
using Connected.Extensions;
using System.Globalization;
namespace Connected
{
@ -7,9 +8,8 @@ namespace Connected
public static CultureInfo DefaultCulture = CultureInfo.CurrentUICulture;
#region --> Date converters
public static Converter<DateTime> IsoDate
=> new()
{ SetFunc = SetIsoDate, GetFunc = GetIsoDate };
public static LambdaConverter<DateTime, string> IsoDate
=> new(SetIsoDate, GetIsoDate);
private static DateTime GetIsoDate(string value)
{
@ -23,11 +23,10 @@ namespace Connected
return value.ToIsoDateString();
}
public static Converter<DateTime?> NullableIsoDate
=> new()
{ SetFunc = SetNullableIsoDate, GetFunc = GetNullableIsoDate };
public static LambdaConverter<DateTime?, string?> NullableIsoDate
=> new(SetNullableIsoDate, GetNullableIsoDate);
private static DateTime? GetNullableIsoDate(string value)
private static DateTime? GetNullableIsoDate(string? value)
{
if (DateTime.TryParse(value, out var dateTime))
return dateTime;

@ -6,15 +6,23 @@ namespace Connected
/// <summary>
/// A ready made DateTime? to string binding converter with configurable date format and culture
/// </summary>
public class NullableDateConverter : Converter<DateTime?>
public class NullableDateConverter : ToStringConverter<DateTime?>
{
public string DateFormat { get; set; }
public NullableDateConverter(string format = "yyyy-MM-dd")
{
DateFormat = format;
SetFunc = OnSet;
GetFunc = OnGet;
}
protected override string? ConvertValue(DateTime? value)
{
return OnSet(value);
}
protected override DateTime? ConvertValueBack(string? value)
{
return OnGet(value);
}
private DateTime? OnGet(string arg)
@ -25,7 +33,7 @@ namespace Connected
}
catch (FormatException e)
{
UpdateGetError(e.Message);
TriggerError(e.Message);
return null;
}
}
@ -40,7 +48,7 @@ namespace Connected
}
catch (FormatException e)
{
UpdateSetError(e.Message);
TriggerError(e.Message);
return null;
}
}
@ -49,15 +57,23 @@ namespace Connected
/// <summary>
/// A ready made DateTime to string binding converter with configurable date format and culture
/// </summary>
public class DateConverter : Converter<DateTime>
public class DateConverter : ToStringConverter<DateTime>
{
public string DateFormat { get; set; } = "yyyy-MM-dd";
public DateConverter(string format)
{
DateFormat = format;
SetFunc = OnSet;
GetFunc = OnGet;
}
protected override string? ConvertValue(DateTime value)
{
return OnSet(value);
}
protected override DateTime ConvertValueBack(string value)
{
return OnGet(value);
}
private DateTime OnGet(string arg)
@ -68,7 +84,7 @@ namespace Connected
}
catch (FormatException e)
{
UpdateGetError(e.Message);
TriggerError(e.Message);
return default;
}
}
@ -81,7 +97,7 @@ namespace Connected
}
catch (FormatException e)
{
UpdateSetError(e.Message);
TriggerError(e.Message);
return null;
}
}

@ -7,13 +7,21 @@ namespace Connected
/// <summary>
/// A universal T to string binding converter
/// </summary>
public class DefaultConverter<T> : Converter<T>
public class DefaultConverter<T> : ToStringConverter<T>
{
public DefaultConverter()
{
SetFunc = ConvertToString;
GetFunc = ConvertFromString;
}
protected override string? ConvertValue(T? value)
{
return ConvertToString(value);
}
protected override T? ConvertValueBack(string? value)
{
return ConvertFromString(value);
}
public string DefaultTimeSpanFormat { get; set; } = "c";
@ -42,91 +50,91 @@ namespace Connected
return (T)(object)true;
if (lowerValue is "false" or "off")
return (T)(object)false;
UpdateGetError("Not a valid boolean");
TriggerError("Not a valid boolean");
}
// sbyte
else if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(sbyte?))
{
if (sbyte.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
UpdateGetError("Not a valid number");
TriggerError("Not a valid number");
}
// byte
else if (typeof(T) == typeof(byte) || typeof(T) == typeof(byte?))
{
if (byte.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
UpdateGetError("Not a valid number");
TriggerError("Not a valid number");
}
// short
else if (typeof(T) == typeof(short) || typeof(T) == typeof(short?))
{
if (short.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
UpdateGetError("Not a valid number");
TriggerError("Not a valid number");
}
// ushort
else if (typeof(T) == typeof(ushort) || typeof(T) == typeof(ushort?))
{
if (ushort.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
UpdateGetError("Not a valid number");
TriggerError("Not a valid number");
}
// int
else if (typeof(T) == typeof(int) || typeof(T) == typeof(int?))
{
if (int.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
UpdateGetError("Not a valid number");
TriggerError("Not a valid number");
}
// uint
else if (typeof(T) == typeof(uint) || typeof(T) == typeof(uint?))
{
if (uint.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
UpdateGetError("Not a valid number");
TriggerError("Not a valid number");
}
// long
else if (typeof(T) == typeof(long) || typeof(T) == typeof(long?))
{
if (long.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
UpdateGetError("Not a valid number");
TriggerError("Not a valid number");
}
// ulong
else if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?))
{
if (ulong.TryParse(value, NumberStyles.Integer | NumberStyles.AllowThousands, Culture, out var parsedValue))
return (T)(object)parsedValue;
UpdateGetError("Not a valid number");
TriggerError("Not a valid number");
}
// float
else if (typeof(T) == typeof(float) || typeof(T) == typeof(float?))
{
if (float.TryParse(value, NumberStyles.Any, Culture, out var parsedValue))
return (T)(object)parsedValue;
UpdateGetError("Not a valid number");
TriggerError("Not a valid number");
}
// double
else if (typeof(T) == typeof(double) || typeof(T) == typeof(double?))
{
if (double.TryParse(value, NumberStyles.Any, Culture, out var parsedValue))
return (T)(object)parsedValue;
UpdateGetError("Not a valid number");
TriggerError("Not a valid number");
}
// decimal
else if (typeof(T) == typeof(decimal) || typeof(T) == typeof(decimal?))
{
if (decimal.TryParse(value, NumberStyles.Any, Culture, out var parsedValue))
return (T)(object)parsedValue;
UpdateGetError("Not a valid number");
TriggerError("Not a valid number");
}
// guid
else if (typeof(T) == typeof(Guid) || typeof(T) == typeof(Guid?))
{
if (Guid.TryParse(value, out var parsedValue))
return (T)(object)parsedValue;
UpdateGetError("Not a valid GUID");
TriggerError("Not a valid GUID");
}
// enum
else if (IsNullableEnum(typeof(T)))
@ -134,13 +142,13 @@ namespace Connected
var enum_type = Nullable.GetUnderlyingType(typeof(T));
if (Enum.TryParse(enum_type, value, out var parsedValue))
return (T)parsedValue;
UpdateGetError("Not a value of " + enum_type.Name);
TriggerError("Not a value of " + enum_type.Name);
}
else if (typeof(T).IsEnum)
{
if (Enum.TryParse(typeof(T), value, out var parsedValue))
return (T)parsedValue;
UpdateGetError("Not a value of " + typeof(T).Name);
TriggerError("Not a value of " + typeof(T).Name);
}
// datetime
else if (typeof(T) == typeof(DateTime) || typeof(T) == typeof(DateTime?))
@ -151,7 +159,7 @@ namespace Connected
}
catch (FormatException)
{
UpdateGetError("Not a valid date time");
TriggerError("Not a valid date time");
}
}
// timespan
@ -163,17 +171,17 @@ namespace Connected
}
catch (FormatException)
{
UpdateGetError("Not a valid time span");
TriggerError("Not a valid time span");
}
}
else
{
UpdateGetError($"Conversion to type {typeof(T)} not implemented");
TriggerError($"Conversion to type {typeof(T)} not implemented");
}
}
catch (Exception e)
{
UpdateGetError("Conversion error: " + e.Message);
TriggerError("Conversion error: " + e.Message);
}
return default(T);
@ -301,7 +309,7 @@ namespace Connected
}
catch (FormatException e)
{
UpdateSetError("Conversion error: " + e.Message);
TriggerError("Conversion error: " + e.Message);
return null;
}
}

@ -17,8 +17,16 @@ namespace Connected
public NumericConverter()
{
SetFunc = OnSet;
GetFunc = OnGet;
}
protected override double ConvertValue(T? value)
{
return OnSet(value);
}
protected override T? ConvertValueBack(double value)
{
return OnGet(value);
}
private T OnGet(double value)
@ -33,42 +41,42 @@ namespace Connected
return (T)(object)value.ToString(Culture);
// sbyte
else if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(sbyte?))
return (T)(object)Convert.ToSByte(value);
return (T)(object)System.Convert.ToSByte(value);
// byte
else if (typeof(T) == typeof(byte) || typeof(T) == typeof(byte?))
return (T)(object)Convert.ToByte(value);
return (T)(object)System.Convert.ToByte(value);
// short
else if (typeof(T) == typeof(short) || typeof(T) == typeof(short?))
return (T)(object)Convert.ToInt16(value);
return (T)(object)System.Convert.ToInt16(value);
// ushort
else if (typeof(T) == typeof(ushort) || typeof(T) == typeof(ushort?))
return (T)(object)Convert.ToUInt16(value);
return (T)(object)System.Convert.ToUInt16(value);
// int
else if (typeof(T) == typeof(int) || typeof(T) == typeof(int?))
return (T)(object)Convert.ToInt32(value);
return (T)(object)System.Convert.ToInt32(value);
// uint
else if (typeof(T) == typeof(uint) || typeof(T) == typeof(uint?))
return (T)(object)Convert.ToUInt32(value);
return (T)(object)System.Convert.ToUInt32(value);
// long
else if (typeof(T) == typeof(long) || typeof(T) == typeof(long?))
return (T)(object)Convert.ToInt64(value);
return (T)(object)System.Convert.ToInt64(value);
// ulong
else if (typeof(T) == typeof(ulong) || typeof(T) == typeof(ulong?))
return (T)(object)Convert.ToUInt64(value);
return (T)(object)System.Convert.ToUInt64(value);
// float
else if (typeof(T) == typeof(float) || typeof(T) == typeof(float?))
return (T)(object)Convert.ToSingle(value);
return (T)(object)System.Convert.ToSingle(value);
// decimal
else if (typeof(T) == typeof(decimal) || typeof(T) == typeof(decimal?))
return (T)(object)Convert.ToDecimal(value);
return (T)(object)System.Convert.ToDecimal(value);
else
{
UpdateGetError($"Conversion to type {typeof(T)} not implemented");
TriggerError($"Conversion to type {typeof(T)} not implemented");
}
}
catch (Exception e)
{
UpdateGetError("Conversion error: " + e.Message);
TriggerError("Conversion error: " + e.Message);
return default(T);
}
return default(T);
@ -90,63 +98,63 @@ namespace Connected
return double.Parse((string)(object)arg, NumberStyles.Any, Culture);
// sbyte
if (typeof(T) == typeof(sbyte))
return Convert.ToDouble((sbyte)(object)arg);
return System.Convert.ToDouble((sbyte)(object)arg);
if (typeof(T) == typeof(sbyte?))
return Convert.ToDouble(((sbyte?)(object)arg).Value);
return System.Convert.ToDouble(((sbyte?)(object)arg).Value);
// byte
if (typeof(T) == typeof(byte))
return Convert.ToDouble((byte)(object)arg);
return System.Convert.ToDouble((byte)(object)arg);
if (typeof(T) == typeof(byte?))
return Convert.ToDouble(((byte?)(object)arg).Value);
return System.Convert.ToDouble(((byte?)(object)arg).Value);
// short
if (typeof(T) == typeof(short))
return Convert.ToDouble((short)(object)arg);
return System.Convert.ToDouble((short)(object)arg);
if (typeof(T) == typeof(short?))
return Convert.ToDouble(((short?)(object)arg).Value);
return System.Convert.ToDouble(((short?)(object)arg).Value);
// ushort
if (typeof(T) == typeof(ushort))
return Convert.ToDouble((ushort)(object)arg);
return System.Convert.ToDouble((ushort)(object)arg);
if (typeof(T) == typeof(ushort?))
return Convert.ToDouble(((ushort?)(object)arg).Value);
return System.Convert.ToDouble(((ushort?)(object)arg).Value);
// int
else if (typeof(T) == typeof(int))
return Convert.ToDouble((int)(object)arg);
return System.Convert.ToDouble((int)(object)arg);
else if (typeof(T) == typeof(int?))
return Convert.ToDouble(((int?)(object)arg).Value);
return System.Convert.ToDouble(((int?)(object)arg).Value);
// uint
else if (typeof(T) == typeof(uint))
return Convert.ToDouble((uint)(object)arg);
return System.Convert.ToDouble((uint)(object)arg);
else if (typeof(T) == typeof(uint?))
return Convert.ToDouble(((uint?)(object)arg).Value);
return System.Convert.ToDouble(((uint?)(object)arg).Value);
// long
else if (typeof(T) == typeof(long))
return Convert.ToDouble((long)(object)arg);
return System.Convert.ToDouble((long)(object)arg);
else if (typeof(T) == typeof(long?))
return Convert.ToDouble(((long?)(object)arg).Value);
return System.Convert.ToDouble(((long?)(object)arg).Value);
// ulong
else if (typeof(T) == typeof(ulong))
return Convert.ToDouble((ulong)(object)arg);
return System.Convert.ToDouble((ulong)(object)arg);
else if (typeof(T) == typeof(ulong?))
return Convert.ToDouble(((ulong?)(object)arg).Value);
return System.Convert.ToDouble(((ulong?)(object)arg).Value);
// float
else if (typeof(T) == typeof(float))
return Convert.ToDouble((float)(object)arg);
return System.Convert.ToDouble((float)(object)arg);
else if (typeof(T) == typeof(float?))
return Convert.ToDouble(((float?)(object)arg).Value);
return System.Convert.ToDouble(((float?)(object)arg).Value);
// decimal
else if (typeof(T) == typeof(decimal))
return Convert.ToDouble((decimal)(object)arg);
return System.Convert.ToDouble((decimal)(object)arg);
else if (typeof(T) == typeof(decimal?))
return Convert.ToDouble(((decimal?)(object)arg).Value);
return System.Convert.ToDouble(((decimal?)(object)arg).Value);
else
{
UpdateSetError("Unable to convert to double from type " + typeof(T).Name);
TriggerError("Unable to convert to double from type " + typeof(T).Name);
return double.NaN;
}
}
catch (FormatException e)
{
UpdateSetError("Conversion error: " + e.Message);
TriggerError("Conversion error: " + e.Message);
return double.NaN;
}
}

@ -1,31 +1,40 @@
namespace Connected
{
public class RangeConverter<T> : Converter<Components.Range<T>>
using Connected.Components;
namespace Connected;
public class RangeConverter<T> : ToStringConverter<Range<T>>
{
readonly DefaultConverter<T> _converter;
public RangeConverter()
{
_converter = new DefaultConverter<T>();
}
SetFunc = OnSet;
GetFunc = OnGet;
protected override string? ConvertValue(Range<T>? value)
{
return OnSet(value);
}
private Components.Range<T> OnGet(string value)
protected override Range<T>? ConvertValueBack(string? value)
{
return OnGet(value);
}
private Range<T> OnGet(string value)
{
if (!Split(value, out var valueStart, out var valueEnd))
return null;
return new Components.Range<T>(_converter.Get(valueStart), _converter.Get(valueEnd));
return new Range<T>(_converter.ConvertBack(valueStart), _converter.ConvertBack(valueEnd));
}
private string OnSet(Components.Range<T> arg)
private string OnSet(Range<T> arg)
{
if (arg == null)
return string.Empty;
return Join(_converter.Set(arg.Start), _converter.Set(arg.End));
return Join(_converter.Convert(arg.Start), _converter.Convert(arg.End));
}
public static string Join(string valueStart, string valueEnd)
@ -58,4 +67,3 @@
return true;
}
}
}

Loading…
Cancel
Save