Sicherung

This commit is contained in:
Maier Stephan SI
2023-01-02 04:33:49 +01:00
parent bea46135fd
commit d01747f75a
284 changed files with 6106 additions and 65112 deletions

View File

@@ -0,0 +1,204 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
namespace Config.Net.Core.Box
{
static class BoxFactory
{
public static Dictionary<string, ResultBox> Discover(Type t, ValueHandler valueHandler, string? basePath)
{
var result = new Dictionary<string, ResultBox>();
DiscoverProperties(t, valueHandler, result, basePath);
DiscoverMethods(t, valueHandler, result);
return result;
}
private static void DiscoverProperties(Type t, ValueHandler valueHandler, Dictionary<string, ResultBox> result, string? basePath)
{
IEnumerable<PropertyInfo> properties = GetHierarchyPublicProperties(t);
foreach (PropertyInfo pi in properties)
{
Type propertyType = pi.PropertyType;
ResultBox rbox;
bool isCollection = false;
if(ResultBox.TryGetCollection(propertyType, out propertyType))
{
if(pi.SetMethod != null)
{
throw new NotSupportedException($"Collection properties cannot have a setter. Detected at '{OptionPath.Combine(basePath, pi.Name)}'");
}
isCollection = true;
}
if(propertyType.GetTypeInfo().IsInterface)
{
rbox = new ProxyResultBox(pi.Name, propertyType);
}
else
{
rbox = new PropertyResultBox(pi.Name, propertyType);
}
ValidateSupportedType(rbox, valueHandler);
AddAttributes(rbox, pi, valueHandler);
//adjust to collection
if(isCollection)
{
rbox = new CollectionResultBox(pi.Name, rbox);
AddAttributes(rbox, pi, valueHandler);
}
result[pi.Name] = rbox;
}
}
private static void DiscoverMethods(Type t, ValueHandler valueHandler, Dictionary<string, ResultBox> result)
{
TypeInfo ti = t.GetTypeInfo();
IEnumerable<MethodInfo> methods = ti.DeclaredMethods.Where(m => !m.IsSpecialName);
foreach (MethodInfo method in methods)
{
var mbox = new MethodResultBox(method);
AddAttributes(mbox, method, valueHandler);
result[mbox.Name] = mbox;
}
}
private static void ValidateSupportedType(ResultBox rb, ValueHandler valueHandler)
{
Type? t = null;
if (rb is PropertyResultBox pbox)
t = rb.ResultBaseType;
if (t != null && !valueHandler.IsSupported(t))
{
throw new NotSupportedException($"type {t} on object '{rb.Name}' is not supported.");
}
}
private static object? GetDefaultValue(Type t)
{
if (t.GetTypeInfo().IsValueType) return Activator.CreateInstance(t);
return null;
}
private static void AddAttributes(ResultBox box, PropertyInfo pi, ValueHandler valueHandler)
{
AddAttributes(box, valueHandler,
pi.GetCustomAttribute<OptionAttribute>(),
pi.GetCustomAttribute<DefaultValueAttribute>());
}
private static void AddAttributes(ResultBox box, MethodInfo mi, ValueHandler valueHandler)
{
AddAttributes(box, valueHandler, mi.GetCustomAttribute<OptionAttribute>(), mi.GetCustomAttribute<DefaultValueAttribute>());
}
private static void AddAttributes(ResultBox box, ValueHandler valueHandler, params Attribute?[] attributes)
{
OptionAttribute? optionAttribute = attributes.OfType<OptionAttribute>().FirstOrDefault();
DefaultValueAttribute? defaultValueAttribute = attributes.OfType<DefaultValueAttribute>().FirstOrDefault();
if (optionAttribute?.Alias != null)
{
box.StoreByName = optionAttribute.Alias;
}
box.DefaultResult = GetDefaultValue(optionAttribute?.DefaultValue, box, valueHandler) ??
GetDefaultValue(defaultValueAttribute?.Value, box, valueHandler) ??
GetDefaultValue(box.ResultType);
}
private static object? GetDefaultValue(object? defaultValue, ResultBox box, ValueHandler valueHandler)
{
object? result = null;
if (defaultValue != null)
{
//validate that types for default value match
Type dvt = defaultValue.GetType();
if (dvt != box.ResultType && dvt != typeof(string))
{
throw new InvalidCastException($"Default value for option {box.Name} is of type {dvt.FullName} whereas the property has type {box.ResultType.FullName}. To fix this, either set default value to type {box.ResultType.FullName} or a string parseable to the target type.");
}
if (box.ResultType != typeof(string) && dvt == typeof(string))
{
valueHandler.TryParse(box.ResultType, (string?)defaultValue, out result);
}
}
if (result == null)
{
result = defaultValue;
}
return result;
}
private static PropertyInfo[] GetHierarchyPublicProperties(Type type)
{
var propertyInfos = new List<PropertyInfo>();
var considered = new List<TypeInfo>();
var queue = new Queue<TypeInfo>();
considered.Add(type.GetTypeInfo());
queue.Enqueue(type.GetTypeInfo());
while (queue.Count > 0)
{
TypeInfo typeInfo = queue.Dequeue();
//add base interfaces to the queue
foreach (Type subInterface in typeInfo.ImplementedInterfaces)
{
TypeInfo subInterfaceTypeInfo = subInterface.GetTypeInfo();
if (considered.Contains(subInterfaceTypeInfo)) continue;
considered.Add(subInterfaceTypeInfo);
queue.Enqueue(subInterfaceTypeInfo);
}
//add base classes to the queue
if (typeInfo.BaseType != null)
{
TypeInfo baseType = typeInfo.BaseType.GetTypeInfo();
if (!considered.Contains(baseType))
{
considered.Add(baseType);
queue.Enqueue(baseType);
}
}
//get properties from the current type
IEnumerable<PropertyInfo> newProperties = typeInfo.DeclaredProperties.Where(p => !propertyInfos.Contains(p));
propertyInfos.InsertRange(0, newProperties);
}
return propertyInfos.ToArray();
}
}
}

View File

@@ -0,0 +1,109 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace Config.Net.Core.Box
{
class CollectionResultBox : ResultBox
{
private readonly ResultBox _elementResultBox;
private string? _basePath;
private DynamicReader? _reader;
public CollectionResultBox(string name, ResultBox elementBox) : base(name, elementBox.ResultType, null)
{
_elementResultBox = elementBox;
}
public ResultBox ElementResultBox => _elementResultBox;
public bool IsInitialised { get; private set; }
public IEnumerable? CollectionInstance { get; private set; }
public void Initialise(string? basePath, int length, DynamicReader reader)
{
_basePath = basePath;
_reader = reader;
CollectionInstance = CreateGenericEnumerable(length);
IsInitialised = true;
}
private IEnumerable? CreateGenericEnumerable(int count)
{
Type t = typeof(DynamicEnumerable<>);
t = t.MakeGenericType(ResultType);
IEnumerable? instance = (IEnumerable?)Activator.CreateInstance(t, count, this);
return instance;
}
private object? ReadAt(int index)
{
return _reader?.Read(ElementResultBox, index);
}
private class DynamicEnumerable<T> : IEnumerable<T>
{
private readonly int _count;
private readonly CollectionResultBox _parent;
public DynamicEnumerable(int count, CollectionResultBox parent)
{
_count = count;
_parent = parent;
}
public IEnumerator<T> GetEnumerator()
{
return new DynamicEnumerator<T>(_count, _parent);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new DynamicEnumerator<T>(_count, _parent);
}
}
private class DynamicEnumerator<T> : IEnumerator<T>
{
private int _index = -1;
private readonly int _count;
private readonly CollectionResultBox _parent;
private T? _current;
public DynamicEnumerator(int count, CollectionResultBox parent)
{
_count = count;
_parent = parent;
}
#pragma warning disable CS8603 // Possible null reference return.
public T Current => _current ?? default(T);
object IEnumerator.Current => _current;
#pragma warning restore CS8603 // Possible null reference return.
public void Dispose()
{
}
public bool MoveNext()
{
_index += 1;
_current = (T?)_parent.ReadAt(_index);
return _index < _count;
}
public void Reset()
{
_index = -1;
}
}
}
}

View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace Config.Net.Core.Box
{
class MethodResultBox : ResultBox
{
public MethodResultBox(MethodInfo methodInfo) : base(GetName(methodInfo), GetReturnType(methodInfo), null)
{
StoreByName = GetStoreName(methodInfo);
IsGettter = IsGet(methodInfo);
}
public bool IsGettter { get; private set; }
/// <summary>
/// Composes a uniqueue method name using method name itself and parameter type names, separated by underscore
/// </summary>
public static string GetName(MethodInfo mi)
{
ParameterInfo[] parameters = mi.GetParameters();
var sb = new StringBuilder();
sb.Append(mi.Name);
foreach (ParameterInfo pi in parameters)
{
sb.Append("-");
sb.Append(pi.ParameterType.ToString());
}
return sb.ToString();
}
public string GetValuePath(object[] arguments)
{
var sb = new StringBuilder();
sb.Append(StoreByName);
bool ignoreLast = !IsGettter;
for (int i = 0; i < arguments.Length - (ignoreLast ? 1 : 0); i++)
{
object value = arguments[i];
if (value == null) continue;
if (sb.Length > 0)
{
sb.Append(OptionPath.Separator);
}
sb.Append(value.ToString());
}
return sb.ToString();
}
private static string GetStoreName(MethodInfo mi)
{
string name = mi.Name;
if (name.StartsWith("get", StringComparison.OrdinalIgnoreCase) ||
name.StartsWith("set", StringComparison.OrdinalIgnoreCase))
{
name = name.Substring(3);
}
return name;
}
private static bool IsGet(MethodInfo mi)
{
return mi.ReturnType != typeof(void);
}
private static Type GetReturnType(MethodInfo mi)
{
ParameterInfo[] parameters = mi.GetParameters();
if (parameters == null || parameters.Length == 0)
{
throw new InvalidOperationException($"method {mi.Name} must have at least one parameter");
}
Type returnType = IsGet(mi) ? mi.ReturnType : parameters[parameters.Length - 1].ParameterType;
return returnType;
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Reflection;
namespace Config.Net.Core.Box
{
class PropertyResultBox : ResultBox
{
public PropertyResultBox(string name, Type resultType) : base(name, resultType, null)
{
}
public static bool IsProperty(MethodInfo mi, out bool isGetter, out string? name)
{
if (mi.Name.StartsWith("get_"))
{
isGetter = true;
name = mi.Name.Substring(4);
return true;
}
if (mi.Name.StartsWith("set_"))
{
isGetter = false;
name = mi.Name.Substring(4);
return true;
}
isGetter = false;
name = null;
return false;
}
public static bool IsProperty(MethodInfo mi, out string? name)
{
if (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_"))
{
name = mi.Name.Substring(4);
return true;
}
name = null;
return false;
}
public static bool IsGetProperty(MethodInfo mi)
{
return mi.Name.StartsWith("get_");
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using Castle.DynamicProxy;
namespace Config.Net.Core.Box
{
class ProxyResultBox : ResultBox
{
private static readonly ProxyGenerator ProxyGenerator = new ProxyGenerator();
private readonly Dictionary<int, object> _indexToProxyInstance = new Dictionary<int, object>();
public ProxyResultBox(string name, Type interfaceType) : base(name, interfaceType, null)
{
}
public bool IsInitialisedAt(int index)
{
return _indexToProxyInstance.ContainsKey(index);
}
public object GetInstanceAt(int index)
{
return _indexToProxyInstance[index];
}
public void InitialiseAt(int index, IoHandler ioHandler, string prefix)
{
object instance = ProxyGenerator.CreateInterfaceProxyWithoutTarget(ResultBaseType,
new InterfaceInterceptor(ResultBaseType, ioHandler, prefix));
_indexToProxyInstance[index] = instance;
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace Config.Net.Core.Box
{
abstract class ResultBox
{
private string? _storeByName;
protected ResultBox(string name, Type resultType, object? defaultResult)
{
Name = name;
ResultType = resultType;
ResultBaseType = GetBaseType(resultType);
DefaultResult = defaultResult;
}
public string Name { get; }
public string StoreByName
{
get => _storeByName ?? Name;
set => _storeByName = value;
}
public Type ResultType { get; }
public Type ResultBaseType { get; }
public object? DefaultResult { get; set; }
#region [ Utility Methods ]
private static Type GetBaseType(Type t)
{
TypeInfo ti = t.GetTypeInfo();
if (ti.IsClass)
{
return t;
}
else
{
if (ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return ti.GenericTypeArguments[0];
}
else
{
return t;
}
}
}
internal static bool TryGetCollection(Type t, out Type elementType)
{
TypeInfo ti = t.GetTypeInfo();
if(ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
elementType = ti.GenericTypeArguments[0];
return true;
}
elementType = t;
return false;
}
#endregion
}
}