mirror of https://github.com/sean-m/McRule.git
Compare commits
4 Commits
4c4d609f5d
...
c806955ce6
Author | SHA1 | Date |
---|---|---|
Sean McArde | c806955ce6 | |
Sean McArde | 1135eea353 | |
Sean McArdle | dfe02c34b6 | |
Sean McArde | 77d6c3f1a6 |
|
@ -0,0 +1,159 @@
|
|||
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
public class DynamicTypeRegistry
|
||||
{
|
||||
|
||||
public static Func<T, T> DynamicSelector<T>(params string[] properties)
|
||||
{
|
||||
var param = Expression.Parameter(typeof(T), "x");
|
||||
var constructor = Expression.New(typeof(T));
|
||||
|
||||
// property initializers from list of property names
|
||||
var bindings = properties.Select(p => p.Trim())
|
||||
.Select(x =>
|
||||
{
|
||||
var sourceProp = typeof(T).GetProperty(x);
|
||||
var sourceValue = Expression.Property(param, sourceProp);
|
||||
return Expression.Bind(sourceProp, sourceValue);
|
||||
}
|
||||
);
|
||||
|
||||
var initializedObject = Expression.MemberInit(constructor, bindings);
|
||||
var lambda = Expression.Lambda<Func<T, T>>(initializedObject, param);
|
||||
|
||||
return lambda.Compile();
|
||||
}
|
||||
|
||||
|
||||
protected class McDynamicType {
|
||||
public string name { get; set;}
|
||||
public TypeBuilder tb {get; set;}
|
||||
Type _builtType;
|
||||
public Type builtType
|
||||
{
|
||||
get {
|
||||
if (_builtType == null) { _builtType = tb.CreateType(); }
|
||||
return _builtType;
|
||||
}
|
||||
}
|
||||
public string[] properties {get; set;}
|
||||
|
||||
public Type buildForType {get; set;}
|
||||
|
||||
public Func<T,T> GetDynamicSelector<T>() {
|
||||
return DynamicSelector<T>(properties);
|
||||
}
|
||||
}
|
||||
|
||||
AssemblyBuilder dynAsm { get; set; }
|
||||
ModuleBuilder dynModule { get; set; }
|
||||
|
||||
Dictionary<string, McDynamicType> registeredTypes = new Dictionary<string, McDynamicType>();
|
||||
|
||||
|
||||
public DynamicTypeRegistry()
|
||||
{
|
||||
dynAsm = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("McDynamicAssembly"), AssemblyBuilderAccess.Run);
|
||||
dynModule = dynAsm.DefineDynamicModule("McTypes");
|
||||
}
|
||||
|
||||
public Func<T, T> DynamicSelector<T>(string dynName)
|
||||
{
|
||||
McDynamicType dynType = null;
|
||||
var sourceType = typeof(T);
|
||||
|
||||
if (registeredTypes.TryGetValue(dynName, out dynType))
|
||||
{
|
||||
return dynType.GetDynamicSelector<T>();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public dynamic CopyFromSourceObject<T>(T source, string dynName)
|
||||
{
|
||||
McDynamicType dynType = null;
|
||||
var sourceType = typeof(T);
|
||||
|
||||
if (registeredTypes.TryGetValue(dynName, out dynType))
|
||||
{
|
||||
var resultType = dynType.builtType;
|
||||
var result = dynAsm.CreateInstance(dynType.name);
|
||||
|
||||
foreach (var p in dynType.properties) {
|
||||
var prop = resultType.GetProperty(p);
|
||||
prop.SetValue(result, sourceType.GetProperty(p).GetValue(source));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void BuildDynamicSubType<T>(string shortName, IEnumerable<string> properties)
|
||||
{
|
||||
var requestedType = typeof(T);
|
||||
var props = properties.ToArray();
|
||||
Array.Sort(props);
|
||||
var dynTypeName = shortName ?? $"{requestedType.Name}_{String.Join('-',props)}"; // TODO make this a concatination of the policy ids for a given predicate and type.
|
||||
var dynType = dynModule.DefineType(dynTypeName);
|
||||
|
||||
foreach (var p in properties)
|
||||
{
|
||||
var reflectedProperty = requestedType.GetProperty(p);
|
||||
|
||||
if (reflectedProperty == null) continue;
|
||||
|
||||
AddProperty(dynType, p, reflectedProperty.PropertyType);
|
||||
}
|
||||
|
||||
var builtType = new McDynamicType();
|
||||
builtType.tb = dynType;
|
||||
builtType.buildForType = typeof(T);
|
||||
builtType.name = dynTypeName;
|
||||
builtType.properties = props;
|
||||
|
||||
registeredTypes.Add(dynTypeName, builtType);
|
||||
}
|
||||
|
||||
public Type GetDynamicObjectByTypeName(string Name)
|
||||
{
|
||||
McDynamicType result = null;
|
||||
if (registeredTypes.TryGetValue(Name, out result))
|
||||
{
|
||||
return result.builtType;
|
||||
}
|
||||
return typeof(Object);
|
||||
}
|
||||
|
||||
private void AddProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
|
||||
{
|
||||
const MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.HideBySig;
|
||||
|
||||
// Backing field
|
||||
FieldBuilder field = typeBuilder.DefineField("_" + propertyName, typeof(string), FieldAttributes.Private);
|
||||
PropertyBuilder property = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType,
|
||||
new[] { propertyType });
|
||||
|
||||
// Getter
|
||||
MethodBuilder getMethodBuilder = typeBuilder.DefineMethod("get_value", getSetAttr, propertyType,
|
||||
Type.EmptyTypes);
|
||||
ILGenerator getIl = getMethodBuilder.GetILGenerator();
|
||||
getIl.Emit(OpCodes.Ldarg_0);
|
||||
getIl.Emit(OpCodes.Ldfld, field);
|
||||
getIl.Emit(OpCodes.Ret);
|
||||
|
||||
// Setter
|
||||
MethodBuilder setMethodBuilder = typeBuilder.DefineMethod("set_value", getSetAttr, null,
|
||||
new[] { propertyType });
|
||||
ILGenerator setIl = setMethodBuilder.GetILGenerator();
|
||||
setIl.Emit(OpCodes.Ldarg_0);
|
||||
setIl.Emit(OpCodes.Ldarg_1);
|
||||
setIl.Emit(OpCodes.Stfld, field);
|
||||
setIl.Emit(OpCodes.Ret);
|
||||
|
||||
property.SetGetMethod(getMethodBuilder);
|
||||
property.SetSetMethod(setMethodBuilder);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ using System.Collections;
|
|||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using ConsoleTables;
|
||||
using McRule;
|
||||
using Newtonsoft.Json;
|
||||
|
@ -49,12 +50,45 @@ public static class MyExtensions {
|
|||
|
||||
table.Write(Format.Minimal);
|
||||
} else {
|
||||
Console.WriteLine(JsonConvert.SerializeObject(thing, Formatting.Indented));
|
||||
Console.WriteLine(ToKvText(thing));
|
||||
}
|
||||
|
||||
end:
|
||||
return thing;
|
||||
}
|
||||
|
||||
public static string ToKvText(this object entry) {
|
||||
if (entry == null) return string.Empty;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
var properties = entry.GetType().GetProperties();
|
||||
var longestPropertyLength = properties.Select(x => x.Name.Length).Max();
|
||||
int indentation = longestPropertyLength + 3;
|
||||
|
||||
foreach (var p in properties) {
|
||||
sb.Append(p.Name.PadRight(longestPropertyLength));
|
||||
sb.Append(" : ");
|
||||
bool multiLine = false;
|
||||
var thing = p.GetValue(entry);
|
||||
if (thing is string) {
|
||||
foreach (var value in (thing ?? "NULL").ToString().Split("\n")) {
|
||||
var strValue = FormatProperty((value) ?? "NULL");
|
||||
if (multiLine) {
|
||||
sb.AppendLine((strValue).PadLeft(indentation + strValue.Length));
|
||||
} else {
|
||||
sb.AppendLine((strValue).PadRight(longestPropertyLength));
|
||||
multiLine = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var value = FormatProperty(thing);
|
||||
sb.AppendLine((value).PadLeft(value.Length));
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format properties for being printed inside a table. For array objects with fewer than 4
|
||||
|
@ -70,18 +104,18 @@ public static class MyExtensions {
|
|||
if (Property is String s) return s;
|
||||
|
||||
if (Property.GetType().GetInterface("IEnumerable") != null) {
|
||||
if (Property.Length < 4) {
|
||||
if (Enumerable.Count(Property) <= 5) {
|
||||
return $"[{String.Join(", ", Property)}]";
|
||||
} else {
|
||||
var elems = new List<string>();
|
||||
var count = 0;
|
||||
foreach (var el in Property) {
|
||||
count++;
|
||||
elems.Add(el);
|
||||
elems.Add(el.ToString());
|
||||
if (count >= 3) break;
|
||||
}
|
||||
|
||||
return $"[{String.Join(", ", elems)}...({Property.Length})]";
|
||||
return $"[{String.Join(", ", elems)}...({Enumerable.Count(Property)})]";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue