|
|
@@ -53,27 +53,10 @@ namespace InABox.Core
|
|
|
[LibraryInitializer]
|
|
|
public static class CoreUtils
|
|
|
{
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- private static readonly Dictionary<string, Type> entities = new Dictionary<string, Type>();
|
|
|
-
|
|
|
- public static Dictionary<string, long> TypeListSummary = new Dictionary<string, long>();
|
|
|
-
|
|
|
- private static readonly ConcurrentDictionary<Type, IColumns> _columnscache = new ConcurrentDictionary<Type, IColumns>();
|
|
|
-
|
|
|
private static readonly Regex StripHtmlStyles = new Regex(@"<style(.|\n)*?</style>", RegexOptions.Compiled);
|
|
|
private static readonly Regex StripHtmlTags = new Regex(@"<(.|\n)*?>", RegexOptions.Compiled);
|
|
|
private static readonly Regex StripSpecialCharacters = new Regex(@"&#(.|\n)*?;| |>|<"", RegexOptions.Compiled);
|
|
|
private static readonly Regex StripLineBreaks = new Regex(@"( |\r?\n|\r|\n)\1+", RegexOptions.Compiled);
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Return all <see cref="Type"/> that have been loaded through use of the <see cref="RegisterClasses(Assembly[])"/> function.
|
|
|
- /// </summary>
|
|
|
- /// <remarks>
|
|
|
- /// This includes every non-abstract type.
|
|
|
- /// </remarks>
|
|
|
- public static IEnumerable<Type> Entities => entities.Values;
|
|
|
|
|
|
public static long GenerateSequence()
|
|
|
{
|
|
|
@@ -262,7 +245,6 @@ namespace InABox.Core
|
|
|
return enumType.GetField(name).GetCustomAttributes(false).OfType<T>().FirstOrDefault();
|
|
|
}
|
|
|
|
|
|
-
|
|
|
public class GenericTypeBuilder
|
|
|
{
|
|
|
public Type Type { get; private set; }
|
|
|
@@ -350,10 +332,44 @@ namespace InABox.Core
|
|
|
return new GenericTypeBuilder(type);
|
|
|
}
|
|
|
|
|
|
+ public static bool ContainsInheritedGenericType(this Type type, Type generic)
|
|
|
+ {
|
|
|
+ if (type == null)
|
|
|
+ return false;
|
|
|
+ if (type.GenericTypeArguments.Contains(generic))
|
|
|
+ return true;
|
|
|
+ if (type.BaseType == null)
|
|
|
+ return false;
|
|
|
+ return type.BaseType.ContainsInheritedGenericType(generic);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static IEnumerable<Type> GetInheritedGenericTypeArguments(this Type type)
|
|
|
+ {
|
|
|
+ if (type == null)
|
|
|
+ return new Type[] { };
|
|
|
+ if (type.GetGenericArguments().Any())
|
|
|
+ return type.GenericTypeArguments;
|
|
|
+ if (type.BaseType == null)
|
|
|
+ return new Type[] { };
|
|
|
+ return type.BaseType.GetInheritedGenericTypeArguments();
|
|
|
+ }
|
|
|
+
|
|
|
#endregion
|
|
|
|
|
|
#region Entities + Types
|
|
|
|
|
|
+ private static readonly Dictionary<string, Type> entities = new Dictionary<string, Type>();
|
|
|
+
|
|
|
+ public static Dictionary<string, long> TypeListSummary = new Dictionary<string, long>();
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Return all <see cref="Type"/> that have been loaded through use of the <see cref="RegisterClasses(Assembly[])"/> function.
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// This includes every non-abstract type.
|
|
|
+ /// </remarks>
|
|
|
+ public static IEnumerable<Type> Entities => entities.Values;
|
|
|
+
|
|
|
public static void RegisterClasses()
|
|
|
{
|
|
|
RegisterClasses(typeof(CoreUtils).Assembly);
|
|
|
@@ -547,181 +563,13 @@ namespace InABox.Core
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
- public static object? ChangeType(object? value, Type type)
|
|
|
- {
|
|
|
- if ((value == null || value is DBNull) && type.GetTypeInfo().IsGenericType)
|
|
|
- return Activator.CreateInstance(type);
|
|
|
-
|
|
|
- if (value == null)
|
|
|
- return null;
|
|
|
-
|
|
|
- if (value is DBNull)
|
|
|
- return null;
|
|
|
-
|
|
|
- if (type == value.GetType())
|
|
|
- return value;
|
|
|
-
|
|
|
- if (type == typeof(bool))
|
|
|
- return value.Equals(bool.TrueString) || value.Equals(1) || value.Equals("1");
|
|
|
-
|
|
|
- if (type.GetTypeInfo().IsEnum)
|
|
|
- {
|
|
|
- if (value.GetType().IsArray)
|
|
|
- return (value as Array).Cast<object>().Select(x => Enum.ToObject(type, x)).ToArray();
|
|
|
- if (value is string)
|
|
|
- return Enum.Parse(type, value as string);
|
|
|
- if (value is IEnumerable<object> list)
|
|
|
- return list.Select(x => Enum.ToObject(type, x)).ToArray();
|
|
|
- return Enum.ToObject(type, value);
|
|
|
- }
|
|
|
-
|
|
|
- if (value is string && type == typeof(Guid))
|
|
|
- try
|
|
|
- {
|
|
|
- return new Guid(value as string);
|
|
|
- }
|
|
|
- catch
|
|
|
- {
|
|
|
- return Guid.Empty;
|
|
|
- }
|
|
|
-
|
|
|
- if (value is string && type == typeof(byte[]))
|
|
|
- return Convert.FromBase64String(value as string);
|
|
|
-
|
|
|
- if (value is IEnumerable objList)
|
|
|
- {
|
|
|
- if(type == typeof(Guid))
|
|
|
- {
|
|
|
- return objList.Cast<object?>().Select(x => ChangeType<Guid>(x)).ToArray();
|
|
|
- }
|
|
|
- else if (type.IsArray)
|
|
|
- {
|
|
|
- var elementType = type.GetElementType();
|
|
|
- var collection = value as ICollection<object?> ?? objList.Cast<object?>().ToList();
|
|
|
-
|
|
|
- var arr = Array.CreateInstance(elementType, collection.Count);
|
|
|
- var i = 0;
|
|
|
- foreach(var val in collection)
|
|
|
- {
|
|
|
- arr.SetValue(ChangeType(val, elementType), i);
|
|
|
- ++i;
|
|
|
- }
|
|
|
- return arr;
|
|
|
- }
|
|
|
- else if (type.IsGenericType)
|
|
|
- {
|
|
|
- var typeDef = type.GetGenericTypeDefinition();
|
|
|
- if(typeDef == typeof(Dictionary<,>))
|
|
|
- {
|
|
|
- var dict = (Activator.CreateInstance(type) as IDictionary)!;
|
|
|
- var dictDef = type.GetSuperclassDefinition(typeof(Dictionary<,>))!;
|
|
|
- var keyType = dictDef.GenericTypeArguments[0];
|
|
|
- var valType = dictDef.GenericTypeArguments[1];
|
|
|
- if(value is IDictionary fromDict)
|
|
|
- {
|
|
|
- foreach(DictionaryEntry entry in fromDict)
|
|
|
- {
|
|
|
- dict[ChangeType(entry.Key, keyType)] = ChangeType(entry.Value, valType);
|
|
|
- }
|
|
|
- }
|
|
|
- return dict;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!type.GetTypeInfo().IsInterface && type.GetTypeInfo().IsGenericType)
|
|
|
- {
|
|
|
- var innerType = type.GetTypeInfo().GetGenericArguments()[0];
|
|
|
- var innerValue = ChangeType(value, innerType);
|
|
|
- return Activator.CreateInstance(type, innerValue);
|
|
|
- }
|
|
|
-
|
|
|
- if (value is byte[] && type == typeof(Guid))
|
|
|
- return new Guid(value as byte[]);
|
|
|
-
|
|
|
- if (value is string && type == typeof(Version))
|
|
|
- return new Version(value as string);
|
|
|
-
|
|
|
- if (value is byte && type == typeof(TimeSpan))
|
|
|
- return new TimeSpan((byte)value);
|
|
|
-
|
|
|
- if (value is string && type == typeof(TimeSpan))
|
|
|
- return TimeSpan.Parse(value as string);
|
|
|
-
|
|
|
- if (value is string && type == typeof(DateTime))
|
|
|
- return DateTime.Parse(value as string);
|
|
|
-
|
|
|
- if (value is bool && type == typeof(string))
|
|
|
- return (bool)value ? bool.TrueString : bool.FalseString;
|
|
|
-
|
|
|
- //if (value is JArray && type == typeof(string[]))
|
|
|
- // return (value as JArray).Select(x => x.ToString()).ToArray();
|
|
|
-
|
|
|
- if(value is FilterConstant constant)
|
|
|
- {
|
|
|
- if (type == typeof(DateTime))
|
|
|
- {
|
|
|
- if (Equals(FilterConstant.Null, value))
|
|
|
- return DateTime.MinValue;
|
|
|
- return value;
|
|
|
- }
|
|
|
- if (type == typeof(Guid) && constant == FilterConstant.Null)
|
|
|
- {
|
|
|
- return Guid.Empty;
|
|
|
- }
|
|
|
- if (type == typeof(double))
|
|
|
- {
|
|
|
- return value;
|
|
|
- // if (Equals(FilterConstant.Zero, value))
|
|
|
- // return (double)0;
|
|
|
- }
|
|
|
- if (type == typeof(int))
|
|
|
- {
|
|
|
- return value;
|
|
|
- // if (Equals(FilterConstant.Zero, value))
|
|
|
- // return 0;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (type == typeof(String))
|
|
|
- return value?.ToString() ?? string.Empty;
|
|
|
-
|
|
|
- if (!(value is IConvertible))
|
|
|
- return value;
|
|
|
-
|
|
|
- return Convert.ChangeType(value, type);
|
|
|
- }
|
|
|
-
|
|
|
- [return: MaybeNull]
|
|
|
- public static T ChangeType<T>(object? value)
|
|
|
- {
|
|
|
- var newValue = ChangeType(value, typeof(T));
|
|
|
- if (newValue is T tValue)
|
|
|
- return tValue;
|
|
|
- return default;
|
|
|
- }
|
|
|
-
|
|
|
- public static string RandomHexString(int length)
|
|
|
- {
|
|
|
- var rdm = new Random((int)DateTime.Now.Ticks);
|
|
|
- var hexValue = "";
|
|
|
- int num;
|
|
|
- for (var i = 0; i < length; i++)
|
|
|
- {
|
|
|
- num = rdm.Next(0, 16);
|
|
|
- hexValue += num.ToString("X1");
|
|
|
- }
|
|
|
-
|
|
|
- return hexValue;
|
|
|
- }
|
|
|
+ #region Properties
|
|
|
|
|
|
- public static bool IsEmpty(this DateTime value)
|
|
|
+ public static decimal GetPropertySequence(Type type, string column)
|
|
|
{
|
|
|
- return value.Ticks <= DateTime.MinValue.AddHours(8).Ticks;
|
|
|
+ return DatabaseSchema.Property(type, column)?.PropertySequence() ?? 999;
|
|
|
}
|
|
|
|
|
|
- #region Properties
|
|
|
-
|
|
|
public static string GetFullPropertyName<Type, TProperty>(Expression<Func<Type, TProperty>> exp, string separator)
|
|
|
{
|
|
|
try
|
|
|
@@ -1167,8 +1015,6 @@ namespace InABox.Core
|
|
|
return (PropertyInfo)Exp.Member;
|
|
|
}
|
|
|
|
|
|
- #endregion
|
|
|
-
|
|
|
public static bool IsCollection(PropertyInfo prop)
|
|
|
{
|
|
|
if (!typeof(string).Equals(prop.PropertyType))
|
|
|
@@ -1185,31 +1031,6 @@ namespace InABox.Core
|
|
|
exp.NodeType == ExpressionType.ConvertChecked;
|
|
|
}
|
|
|
|
|
|
- //public static Dictionary<String, Type> PropertyList(Type T, bool Public = true, bool Static = false)
|
|
|
- //{
|
|
|
- // Dictionary<String, Type> properties = new Dictionary<String, Type>();
|
|
|
- // foreach (PropertyInfo prop in T.GetRuntimeProperties())
|
|
|
- // {
|
|
|
- // if ((prop.GetMethod.IsPublic == Public) && (prop.GetMethod.IsStatic == Static))
|
|
|
- // {
|
|
|
- // if (prop.PropertyType.GetTypeInfo().IsAssignableFrom(typeof(Entity).GetTypeInfo()))
|
|
|
- // {
|
|
|
- // Dictionary<String, Type> props = PropertyList(prop.PropertyType);
|
|
|
- // foreach (String key in props.Keys)
|
|
|
- // {
|
|
|
- // properties[String.Format("{0}.{1}", prop.Name, key)] = props[key];
|
|
|
- // }
|
|
|
- // }
|
|
|
- // else if (!prop.PropertyType.Equals(typeof(Guid)))
|
|
|
- // {
|
|
|
- // properties[prop.Name] = prop.GetType();
|
|
|
- // }
|
|
|
- // }
|
|
|
- // }
|
|
|
- // return properties;
|
|
|
- //}
|
|
|
-
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Get all the child properties of an object that match a given type
|
|
|
/// </summary>
|
|
|
@@ -1263,6 +1084,221 @@ namespace InABox.Core
|
|
|
return lists.ToArray();
|
|
|
}
|
|
|
|
|
|
+ private static readonly ConcurrentDictionary<Type, IColumns> _columnscache = new ConcurrentDictionary<Type, IColumns>();
|
|
|
+
|
|
|
+ public static Columns<T> GetColumns<T>(Columns<T>? columns)
|
|
|
+ => (GetColumns(typeof(T), columns) as Columns<T>)!;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Ensures that <paramref name="columns"/> is not <see langword="null"/>, by setting it to <b>all</b> columns that are
|
|
|
+ /// writable, serializable and persistable if it is.<br/>
|
|
|
+ /// If <paramref name="columns"/> is not <see langword="null"/>, it is returned unchanged.
|
|
|
+ /// </summary>
|
|
|
+ public static IColumns GetColumns(Type T, IColumns? columns)
|
|
|
+ {
|
|
|
+ if (columns == null || columns.Count == 0)
|
|
|
+ {
|
|
|
+ if (!_columnscache.TryGetValue(T, out var result))
|
|
|
+ {
|
|
|
+ result = Columns.None(T);
|
|
|
+ foreach(var prop in DatabaseSchema.Properties(T))
|
|
|
+ {
|
|
|
+ if (prop is StandardProperty stdProp
|
|
|
+ && (!stdProp.IsPersistable || !stdProp.IsSerializable || !stdProp.Property.CanWrite))
|
|
|
+ continue;
|
|
|
+ result.Add(prop);
|
|
|
+ }
|
|
|
+ _columnscache[T] = result;
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ return columns;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Return a list of the names of the columns of <paramref name="T"/> which are serializable, persistable and writable (i.e., a
|
|
|
+ /// <see langword="null"/> set of columns), <i>and</i> that match the <paramref name="Predicate"/>.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="T"></param>
|
|
|
+ /// <param name="Predicate"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public static List<string> GetColumnNames(Type T, Func<IProperty, bool> predicate)
|
|
|
+ {
|
|
|
+ var result = new List<string>();
|
|
|
+
|
|
|
+ foreach(var prop in DatabaseSchema.Properties(T))
|
|
|
+ {
|
|
|
+ if ((prop is StandardProperty stdProp && (!stdProp.IsPersistable || !stdProp.IsSerializable || !stdProp.Property.CanWrite))
|
|
|
+ || !predicate(prop))
|
|
|
+ continue;
|
|
|
+ result.Add(prop.Name);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Type Utilities
|
|
|
+
|
|
|
+ public static object? ChangeType(object? value, Type type)
|
|
|
+ {
|
|
|
+ if ((value == null || value is DBNull) && type.GetTypeInfo().IsGenericType)
|
|
|
+ return Activator.CreateInstance(type);
|
|
|
+
|
|
|
+ if (value == null)
|
|
|
+ return null;
|
|
|
+
|
|
|
+ if (value is DBNull)
|
|
|
+ return null;
|
|
|
+
|
|
|
+ if (type == value.GetType())
|
|
|
+ return value;
|
|
|
+
|
|
|
+ if (type == typeof(bool))
|
|
|
+ return value.Equals(bool.TrueString) || value.Equals(1) || value.Equals("1");
|
|
|
+
|
|
|
+ if (type.GetTypeInfo().IsEnum)
|
|
|
+ {
|
|
|
+ if (value.GetType().IsArray)
|
|
|
+ return (value as Array).Cast<object>().Select(x => Enum.ToObject(type, x)).ToArray();
|
|
|
+ if (value is string)
|
|
|
+ return Enum.Parse(type, value as string);
|
|
|
+ if (value is IEnumerable<object> list)
|
|
|
+ return list.Select(x => Enum.ToObject(type, x)).ToArray();
|
|
|
+ return Enum.ToObject(type, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (value is string && type == typeof(Guid))
|
|
|
+ try
|
|
|
+ {
|
|
|
+ return new Guid(value as string);
|
|
|
+ }
|
|
|
+ catch
|
|
|
+ {
|
|
|
+ return Guid.Empty;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (value is string && type == typeof(byte[]))
|
|
|
+ return Convert.FromBase64String(value as string);
|
|
|
+
|
|
|
+ if (value is IEnumerable objList)
|
|
|
+ {
|
|
|
+ if(type == typeof(Guid))
|
|
|
+ {
|
|
|
+ return objList.Cast<object?>().Select(x => ChangeType<Guid>(x)).ToArray();
|
|
|
+ }
|
|
|
+ else if (type.IsArray)
|
|
|
+ {
|
|
|
+ var elementType = type.GetElementType();
|
|
|
+ var collection = value as ICollection<object?> ?? objList.Cast<object?>().ToList();
|
|
|
+
|
|
|
+ var arr = Array.CreateInstance(elementType, collection.Count);
|
|
|
+ var i = 0;
|
|
|
+ foreach(var val in collection)
|
|
|
+ {
|
|
|
+ arr.SetValue(ChangeType(val, elementType), i);
|
|
|
+ ++i;
|
|
|
+ }
|
|
|
+ return arr;
|
|
|
+ }
|
|
|
+ else if (type.IsGenericType)
|
|
|
+ {
|
|
|
+ var typeDef = type.GetGenericTypeDefinition();
|
|
|
+ if(typeDef == typeof(Dictionary<,>))
|
|
|
+ {
|
|
|
+ var dict = (Activator.CreateInstance(type) as IDictionary)!;
|
|
|
+ var dictDef = type.GetSuperclassDefinition(typeof(Dictionary<,>))!;
|
|
|
+ var keyType = dictDef.GenericTypeArguments[0];
|
|
|
+ var valType = dictDef.GenericTypeArguments[1];
|
|
|
+ if(value is IDictionary fromDict)
|
|
|
+ {
|
|
|
+ foreach(DictionaryEntry entry in fromDict)
|
|
|
+ {
|
|
|
+ dict[ChangeType(entry.Key, keyType)] = ChangeType(entry.Value, valType);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return dict;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!type.GetTypeInfo().IsInterface && type.GetTypeInfo().IsGenericType)
|
|
|
+ {
|
|
|
+ var innerType = type.GetTypeInfo().GetGenericArguments()[0];
|
|
|
+ var innerValue = ChangeType(value, innerType);
|
|
|
+ return Activator.CreateInstance(type, innerValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (value is byte[] && type == typeof(Guid))
|
|
|
+ return new Guid(value as byte[]);
|
|
|
+
|
|
|
+ if (value is string && type == typeof(Version))
|
|
|
+ return new Version(value as string);
|
|
|
+
|
|
|
+ if (value is byte && type == typeof(TimeSpan))
|
|
|
+ return new TimeSpan((byte)value);
|
|
|
+
|
|
|
+ if (value is string && type == typeof(TimeSpan))
|
|
|
+ return TimeSpan.Parse(value as string);
|
|
|
+
|
|
|
+ if (value is string && type == typeof(DateTime))
|
|
|
+ return DateTime.Parse(value as string);
|
|
|
+
|
|
|
+ if (value is bool && type == typeof(string))
|
|
|
+ return (bool)value ? bool.TrueString : bool.FalseString;
|
|
|
+
|
|
|
+ //if (value is JArray && type == typeof(string[]))
|
|
|
+ // return (value as JArray).Select(x => x.ToString()).ToArray();
|
|
|
+
|
|
|
+ if(value is FilterConstant constant)
|
|
|
+ {
|
|
|
+ if (type == typeof(DateTime))
|
|
|
+ {
|
|
|
+ if (Equals(FilterConstant.Null, value))
|
|
|
+ return DateTime.MinValue;
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+ if (type == typeof(Guid) && constant == FilterConstant.Null)
|
|
|
+ {
|
|
|
+ return Guid.Empty;
|
|
|
+ }
|
|
|
+ if (type == typeof(double))
|
|
|
+ {
|
|
|
+ return value;
|
|
|
+ // if (Equals(FilterConstant.Zero, value))
|
|
|
+ // return (double)0;
|
|
|
+ }
|
|
|
+ if (type == typeof(int))
|
|
|
+ {
|
|
|
+ return value;
|
|
|
+ // if (Equals(FilterConstant.Zero, value))
|
|
|
+ // return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (type == typeof(String))
|
|
|
+ return value?.ToString() ?? string.Empty;
|
|
|
+
|
|
|
+ if (!(value is IConvertible))
|
|
|
+ return value;
|
|
|
+
|
|
|
+ return Convert.ChangeType(value, type);
|
|
|
+ }
|
|
|
+
|
|
|
+ [return: MaybeNull]
|
|
|
+ public static T ChangeType<T>(object? value)
|
|
|
+ {
|
|
|
+ var newValue = ChangeType(value, typeof(T));
|
|
|
+ if (newValue is T tValue)
|
|
|
+ return tValue;
|
|
|
+ return default;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// [ <c>public static object GetDefault(this Type type)</c> ]
|
|
|
/// <para></para>
|
|
|
@@ -1386,24 +1422,6 @@ namespace InABox.Core
|
|
|
}
|
|
|
|
|
|
|
|
|
- //public static TimeSpan Round(this TimeSpan timespan, TimeSpan interval)
|
|
|
- //{
|
|
|
-
|
|
|
- // // The round number, here is a quarter... in seconds
|
|
|
- // double Round = interval.TotalSeconds;
|
|
|
-
|
|
|
- // // Count of round number in this total Seconds...
|
|
|
- // double CountRound = (timespan.TotalSeconds / Round);
|
|
|
-
|
|
|
- // // The main formula to calculate round time...
|
|
|
- // int Sec = (int)(Math.Truncate(CountRound + 0.5) * Round);
|
|
|
-
|
|
|
- // // Now show the result...
|
|
|
- // TimeSpan tRes = new TimeSpan(0, 0, Sec);
|
|
|
-
|
|
|
- // return tRes;
|
|
|
- //}
|
|
|
-
|
|
|
public static bool IsNumeric(this Type dataType)
|
|
|
{
|
|
|
return dataType.IsFloatingPoint() || dataType.IsOrdinal()
|
|
|
@@ -1438,6 +1456,33 @@ namespace InABox.Core
|
|
|
|| dataType == typeof(sbyte);
|
|
|
}
|
|
|
|
|
|
+ public static string GetCaption(this Enum enumValue, bool usedefault = true)
|
|
|
+ {
|
|
|
+ var attribute = enumValue.GetCustomAttribute<Caption>();
|
|
|
+ return attribute?.Text
|
|
|
+ ?? (usedefault ? enumValue.ToString() : "");
|
|
|
+ }
|
|
|
+
|
|
|
+ public static string GetCaption(this Type type, bool usedefault = true)
|
|
|
+ {
|
|
|
+ var attribute = type.GetCustomAttribute<Caption>();
|
|
|
+ var result = attribute != null
|
|
|
+ ? attribute.Text
|
|
|
+ : usedefault
|
|
|
+ ? type.Name
|
|
|
+ : "";
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ public static string? GetCaptionOrNull(this Type type, bool inherit = false)
|
|
|
+ {
|
|
|
+ return type.GetCustomAttribute<Caption>(inherit)?.Text;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Miscellaneous
|
|
|
+
|
|
|
//Extension method for MailMessage to save to a file on disk
|
|
|
public static string GenerateEMLFile(this MailMessage message, bool addUnsentHeader = true)
|
|
|
{
|
|
|
@@ -1476,197 +1521,9 @@ namespace InABox.Core
|
|
|
return filename;
|
|
|
}
|
|
|
|
|
|
- public static Columns<T> GetColumns<T>(Columns<T>? columns)
|
|
|
- => (GetColumns(typeof(T), columns) as Columns<T>)!;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Ensures that <paramref name="columns"/> is not <see langword="null"/>, by setting it to <b>all</b> columns that are
|
|
|
- /// writable, serializable and persistable if it is.<br/>
|
|
|
- /// If <paramref name="columns"/> is not <see langword="null"/>, it is returned unchanged.
|
|
|
- /// </summary>
|
|
|
- public static IColumns GetColumns(Type T, IColumns? columns)
|
|
|
- {
|
|
|
- if (columns == null || columns.Count == 0)
|
|
|
- {
|
|
|
- if (!_columnscache.TryGetValue(T, out var result))
|
|
|
- {
|
|
|
- result = Columns.None(T);
|
|
|
- foreach(var prop in DatabaseSchema.Properties(T))
|
|
|
- {
|
|
|
- if (prop is StandardProperty stdProp
|
|
|
- && (!stdProp.IsPersistable || !stdProp.IsSerializable || !stdProp.Property.CanWrite))
|
|
|
- continue;
|
|
|
- result.Add(prop);
|
|
|
- }
|
|
|
- _columnscache[T] = result;
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- return columns;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Return a list of the names of the columns of <paramref name="T"/> which are serializable, persistable and writable (i.e., a
|
|
|
- /// <see langword="null"/> set of columns), <i>and</i> that match the <paramref name="Predicate"/>.
|
|
|
- /// </summary>
|
|
|
- /// <param name="T"></param>
|
|
|
- /// <param name="Predicate"></param>
|
|
|
- /// <returns></returns>
|
|
|
- public static List<string> GetColumnNames(Type T, Func<IProperty, bool> predicate)
|
|
|
- {
|
|
|
- var result = new List<string>();
|
|
|
-
|
|
|
- foreach(var prop in DatabaseSchema.Properties(T))
|
|
|
- {
|
|
|
- if ((prop is StandardProperty stdProp && (!stdProp.IsPersistable || !stdProp.IsSerializable || !stdProp.Property.CanWrite))
|
|
|
- || !predicate(prop))
|
|
|
- continue;
|
|
|
- result.Add(prop.Name);
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- public static string TypedValueToString(object value)
|
|
|
- {
|
|
|
- var type = value.GetType();
|
|
|
- var Culture = CultureInfo.CurrentCulture;
|
|
|
-
|
|
|
- if (type == typeof(string))
|
|
|
- return (string)value;
|
|
|
-
|
|
|
- if (type == typeof(string[]))
|
|
|
- return Serialization.Serialize(value);
|
|
|
-
|
|
|
- if (IsNumeric(type))
|
|
|
- return string.Format(Culture.NumberFormat, "{0}", value);
|
|
|
-
|
|
|
- // TimeSpan - use constant pattern
|
|
|
- if (type == typeof(TimeSpan))
|
|
|
- return string.Format("{0:c}", value);
|
|
|
-
|
|
|
- // DateTime - use round-trip pattern
|
|
|
- if (type == typeof(DateTime))
|
|
|
- return string.Format("{0:o}", value);
|
|
|
-
|
|
|
- if (type == typeof(Guid))
|
|
|
- return ((Guid)value).ToString();
|
|
|
-
|
|
|
- var converter = TypeDescriptor.GetConverter(type);
|
|
|
- if (converter != null && converter.CanConvertTo(typeof(string)))
|
|
|
- return converter.ConvertToString(value);
|
|
|
-
|
|
|
- throw new Exception(string.Format("Cannot Convert {0} to String", type.Name));
|
|
|
- }
|
|
|
-
|
|
|
- public static object? StringToTypedValue(string value, Type type)
|
|
|
- {
|
|
|
- var Culture = CultureInfo.CurrentCulture;
|
|
|
-
|
|
|
- if (type == typeof(object) && string.IsNullOrEmpty(value))
|
|
|
- return null;
|
|
|
-
|
|
|
- if (type == typeof(string))
|
|
|
- return value;
|
|
|
-
|
|
|
- if (type == typeof(string[]))
|
|
|
- return Serialization.Deserialize<string[]>(value);
|
|
|
-
|
|
|
- if (type == typeof(sbyte))
|
|
|
- return sbyte.Parse(value, NumberStyles.Integer, Culture);
|
|
|
-
|
|
|
- if (type == typeof(byte))
|
|
|
- return byte.Parse(value, NumberStyles.Integer, Culture);
|
|
|
-
|
|
|
- if (type == typeof(ushort))
|
|
|
- return ushort.Parse(value, NumberStyles.Integer, Culture);
|
|
|
-
|
|
|
- if (type == typeof(uint))
|
|
|
- return uint.Parse(value, NumberStyles.Integer, Culture);
|
|
|
-
|
|
|
- if (type == typeof(ulong))
|
|
|
- return ulong.Parse(value, NumberStyles.Integer, Culture);
|
|
|
-
|
|
|
- if (type == typeof(short))
|
|
|
- return short.Parse(value, NumberStyles.Integer, Culture);
|
|
|
-
|
|
|
- if (type == typeof(int))
|
|
|
- return int.Parse(value, NumberStyles.Integer, Culture);
|
|
|
-
|
|
|
- if (type == typeof(long))
|
|
|
- return long.Parse(value, NumberStyles.Integer, Culture);
|
|
|
-
|
|
|
- if (type == typeof(float))
|
|
|
- return float.Parse(value, NumberStyles.Any, Culture);
|
|
|
-
|
|
|
- if (type == typeof(double))
|
|
|
- return double.Parse(value, NumberStyles.Any, Culture);
|
|
|
-
|
|
|
- if (type == typeof(decimal))
|
|
|
- return decimal.Parse(value, NumberStyles.Any, Culture);
|
|
|
-
|
|
|
- if (type == typeof(TimeSpan))
|
|
|
- return TimeSpan.ParseExact(value, "c", null);
|
|
|
-
|
|
|
- if (type == typeof(DateTime))
|
|
|
- return DateTime.ParseExact(value, "o", null);
|
|
|
-
|
|
|
- if (type == typeof(Guid))
|
|
|
- return Guid.Parse(value);
|
|
|
-
|
|
|
- var converter = TypeDescriptor.GetConverter(type);
|
|
|
- if (converter != null && converter.CanConvertFrom(typeof(string)))
|
|
|
- return converter.ConvertFromString(value);
|
|
|
-
|
|
|
- throw new Exception(string.Format("Cannot Convert String to {0}", type.Name));
|
|
|
- }
|
|
|
-
|
|
|
- public static bool ContainsInheritedGenericType(this Type type, Type generic)
|
|
|
- {
|
|
|
- if (type == null)
|
|
|
- return false;
|
|
|
- if (type.GenericTypeArguments.Contains(generic))
|
|
|
- return true;
|
|
|
- if (type.BaseType == null)
|
|
|
- return false;
|
|
|
- return type.BaseType.ContainsInheritedGenericType(generic);
|
|
|
- }
|
|
|
-
|
|
|
- public static IEnumerable<Type> GetInheritedGenericTypeArguments(this Type type)
|
|
|
- {
|
|
|
- if (type == null)
|
|
|
- return new Type[] { };
|
|
|
- if (type.GetGenericArguments().Any())
|
|
|
- return type.GenericTypeArguments;
|
|
|
- if (type.BaseType == null)
|
|
|
- return new Type[] { };
|
|
|
- return type.BaseType.GetInheritedGenericTypeArguments();
|
|
|
- }
|
|
|
- //static readonly Regex StripQuotes = new Regex(@""", RegexOptions.Compiled);
|
|
|
-
|
|
|
- public static string GetCaption(this Enum enumValue, bool usedefault = true)
|
|
|
- {
|
|
|
- var attribute = enumValue.GetCustomAttribute<Caption>();
|
|
|
- return attribute?.Text
|
|
|
- ?? (usedefault ? enumValue.ToString() : "");
|
|
|
- }
|
|
|
+ #endregion
|
|
|
|
|
|
- public static string GetCaption(this Type type, bool usedefault = true)
|
|
|
- {
|
|
|
- var attribute = type.GetCustomAttribute<Caption>();
|
|
|
- var result = attribute != null
|
|
|
- ? attribute.Text
|
|
|
- : usedefault
|
|
|
- ? type.Name
|
|
|
- : "";
|
|
|
- return result;
|
|
|
- }
|
|
|
- public static string? GetCaptionOrNull(this Type type, bool inherit = false)
|
|
|
- {
|
|
|
- return type.GetCustomAttribute<Caption>(inherit)?.Text;
|
|
|
- }
|
|
|
+ #region Filesystem + Paths
|
|
|
|
|
|
public static string SanitiseFileName(string filename)
|
|
|
{
|
|
|
@@ -1690,11 +1547,6 @@ namespace InABox.Core
|
|
|
return sanitisedNamePart;
|
|
|
}
|
|
|
|
|
|
- public static decimal GetPropertySequence(Type type, string column)
|
|
|
- {
|
|
|
- return DatabaseSchema.Property(type, column)?.PropertySequence() ?? 999;
|
|
|
- }
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Gets the AppData folder for this application.
|
|
|
/// </summary>
|
|
|
@@ -1744,11 +1596,9 @@ namespace InABox.Core
|
|
|
return Path.Combine(GetPath(), string.Format("{0:yyyy-MM-dd}.log", DateTime.Today));
|
|
|
}
|
|
|
|
|
|
- public static string SplitCamelCase(this string value)
|
|
|
- {
|
|
|
- return string.Join(" ", Regex.Split(value, @"(?<!^)(?=[A-Z](?![A-Z]|$))")).Replace(" ", " ");
|
|
|
- }
|
|
|
+ #endregion
|
|
|
|
|
|
+ #region Primitive Type Utilities
|
|
|
|
|
|
public static bool IsEffectivelyEqual(this double a, double b, double threshold = 0.00001F)
|
|
|
{
|
|
|
@@ -1764,12 +1614,45 @@ namespace InABox.Core
|
|
|
{
|
|
|
return b - a > threshold;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ public static bool IsEmpty(this DateTime value)
|
|
|
+ {
|
|
|
+ return value.Ticks <= DateTime.MinValue.AddHours(8).Ticks;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
|
|
|
+ {
|
|
|
+ int diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7;
|
|
|
+ return dt.AddDays(-1 * diff).Date;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static DateTime StartOfMonth(this DateTime dt)
|
|
|
+ {
|
|
|
+ return new DateTime(dt.Year, dt.Month, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static DateTime StartOfYear(this DateTime dt)
|
|
|
+ {
|
|
|
+ return new DateTime(dt.Year, 1, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ private const decimal _hoursPerTick = 1.0M / TimeSpan.TicksPerHour;
|
|
|
+
|
|
|
+ public static decimal TotalHoursDecimal(this TimeSpan time)
|
|
|
+ {
|
|
|
+ return time.Ticks * _hoursPerTick;
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Entity Links
|
|
|
+
|
|
|
public static bool IsEntityLinkValid<T, U>(this CoreRow arg, Expression<Func<T, U>> expression) where U : IEntityLink
|
|
|
{
|
|
|
var col = CoreUtils.GetFullPropertyName(expression, ".");
|
|
|
return arg.Get<Guid>(col + ".ID") != Guid.Empty && arg.Get<Guid>(col + ".Deleted") == Guid.Empty;
|
|
|
}
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Gets the ID of an entity link of an entity, doing a validity check (see <see cref="IsEntityLinkValid{T, U}(CoreRow, Expression{Func{T, U}})"/>)
|
|
|
/// </summary>
|
|
|
@@ -1821,6 +1704,8 @@ namespace InABox.Core
|
|
|
return filter.Filter.NotLinkValid();
|
|
|
}
|
|
|
|
|
|
+ #endregion
|
|
|
+
|
|
|
#region DeepClone Utility
|
|
|
|
|
|
//public static object Clone(object from)
|
|
|
@@ -2368,6 +2253,8 @@ namespace InABox.Core
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
+ #region Logging
|
|
|
+
|
|
|
public static string FormatException(Exception err)
|
|
|
{
|
|
|
var messages = new List<string>();
|
|
|
@@ -2385,6 +2272,8 @@ namespace InABox.Core
|
|
|
Logger.Send(LogType.Error, userid, (extra != null ? $"{extra}: " : "") + CoreUtils.FormatException(err), transaction: logger?.Transaction);
|
|
|
}
|
|
|
|
|
|
+ #endregion
|
|
|
+
|
|
|
#region OneToMany Relationships
|
|
|
|
|
|
public static Type? GetOneToMany(Type TMany, Type TOne)
|
|
|
@@ -2444,7 +2333,9 @@ namespace InABox.Core
|
|
|
);
|
|
|
}
|
|
|
#endregion
|
|
|
-
|
|
|
+
|
|
|
+ #region BaseObject Utilities
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Get a dictionary of values representing the current state of an entity
|
|
|
/// </summary>
|
|
|
@@ -2519,23 +2410,8 @@ namespace InABox.Core
|
|
|
MergeChanges(previous, current, result);
|
|
|
return result;
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
- public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
|
|
|
- {
|
|
|
- int diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7;
|
|
|
- return dt.AddDays(-1 * diff).Date;
|
|
|
- }
|
|
|
-
|
|
|
- public static DateTime StartOfMonth(this DateTime dt)
|
|
|
- {
|
|
|
- return new DateTime(dt.Year, dt.Month, 1);
|
|
|
- }
|
|
|
|
|
|
- public static DateTime StartOfYear(this DateTime dt)
|
|
|
- {
|
|
|
- return new DateTime(dt.Year, 1, 1);
|
|
|
- }
|
|
|
+ #endregion
|
|
|
|
|
|
#region String Utilities
|
|
|
|
|
|
@@ -2650,6 +2526,25 @@ namespace InABox.Core
|
|
|
return value;
|
|
|
}
|
|
|
|
|
|
+ public static string RandomHexString(int length)
|
|
|
+ {
|
|
|
+ var rdm = new Random((int)DateTime.Now.Ticks);
|
|
|
+ var hexValue = "";
|
|
|
+ int num;
|
|
|
+ for (var i = 0; i < length; i++)
|
|
|
+ {
|
|
|
+ num = rdm.Next(0, 16);
|
|
|
+ hexValue += num.ToString("X1");
|
|
|
+ }
|
|
|
+
|
|
|
+ return hexValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static string SplitCamelCase(this string value)
|
|
|
+ {
|
|
|
+ return string.Join(" ", Regex.Split(value, @"(?<!^)(?=[A-Z](?![A-Z]|$))")).Replace(" ", " ");
|
|
|
+ }
|
|
|
+
|
|
|
#endregion
|
|
|
|
|
|
#region IEnumerable Utilities
|