Sicherung
This commit is contained in:
204
Config.Net/Core/Box/BoxFactory.cs
Normal file
204
Config.Net/Core/Box/BoxFactory.cs
Normal 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
109
Config.Net/Core/Box/CollectionResultBox.cs
Normal file
109
Config.Net/Core/Box/CollectionResultBox.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
87
Config.Net/Core/Box/MethodResultBox.cs
Normal file
87
Config.Net/Core/Box/MethodResultBox.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
Config.Net/Core/Box/PropertyResultBox.cs
Normal file
51
Config.Net/Core/Box/PropertyResultBox.cs
Normal 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_");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
34
Config.Net/Core/Box/ProxyResultBox.cs
Normal file
34
Config.Net/Core/Box/ProxyResultBox.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
73
Config.Net/Core/Box/ResultBox.cs
Normal file
73
Config.Net/Core/Box/ResultBox.cs
Normal 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user