Skip to content

Instantly share code, notes, and snippets.

@Warpten
Created October 1, 2014 15:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Warpten/5e54d03a8ba9243deb71 to your computer and use it in GitHub Desktop.
Save Warpten/5e54d03a8ba9243deb71 to your computer and use it in GitHub Desktop.
using System;
using System.Xml;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;
using DBFilesClient.NET;
namespace ADBC2
{
public static class XmlDefinitionReader
{
static XmlDocument _reader = null;
static IDictionary<string, DynamicStructure> _structures = new Dictionary<string, DynamicStructure>();
public static void Load(string fileName, uint clientBuild)
{
_structures.Clear();
_reader = new XmlDocument();
_reader.Load(fileName);
GenerateTypes(clientBuild);
}
public static int Count { get { return _structures.Count; } }
public static IDictionary<string, DynamicStructure> GetStructures()
{
return _structures;
}
public static DynamicStructure GetStructure(string dbcName)
{
return _structures[dbcName];
}
static bool GenerateTypes(uint build)
{
if (!_reader["Definitions"].HasChildNodes)
return false;
var nodes = _reader["Definitions"].GetElementsByTagName(@"file");
if (nodes.Count == 0)
return false;
var filteredNodes = new List<XmlNode>();
for (var i = 0; i < nodes.Count; ++i)
if (nodes[i].Attributes["build"].Value == build.ToString())
filteredNodes.Add(nodes[i]);
if (filteredNodes.Count == 0)
return false;
// Construct type infos
foreach (var node in filteredNodes) {
var childNodes = node.ChildNodes;
var fileName = node.Attributes["name"].Value;
_structures[fileName] = new DynamicStructure();
foreach (XmlNode childNode in childNodes)
{
if (childNode.Name != "field")
continue;
dynamic fieldValue; // Triggers type
switch (childNode.Attributes["type"].Value)
{
case "u32": case "uint32": case "uint":
fieldValue = (uint)0;
break;
case "i32": case "int32": case "int":
fieldValue = (int)0;
break;
case "u16": case "uint16": case "ushort":
fieldValue = (ushort)1;
break;
case "i16": case "int16": case "short":
fieldValue = (short)1;
break;
case "u8": case "uint8": case "byte":
fieldValue = (byte)1;
break;
case "i8": case "int8": case "sbyte":
fieldValue = (sbyte)1;
break;
case "double":
fieldValue = (double)1;
break;
case "float":
fieldValue = (float)1;
break;
case "string":
fieldValue = "";
break;
default:
throw new ArgumentOutOfRangeException(String.Format("Field type {0} is not handled.", childNode.Attributes["type"]));
}
_structures[fileName][childNode.Attributes["name"].Value] = fieldValue;
}
}
return true;
}
}
public class DynamicStructure : DynamicObject
{
// Yes, hacks.
[StoragePresence(StoragePresenceOption.Exclude)]
IDictionary<string, dynamic> _values;
public DynamicStructure()
{
_values = new Dictionary<string, dynamic>();
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out dynamic result)
{
var list = new List<dynamic>();
foreach (var index in indexes)
list.Add(_values.Values.ElementAt((int)index));
result = list.ToArray();
return true;
}
public DynamicStructure(IDictionary<string, dynamic> values)
{
_values = values;
}
public override bool TryGetMember(GetMemberBinder binder, out dynamic result)
{
if (_values.ContainsKey(binder.Name))
{
result = _values[binder.Name];
return true;
}
result = null;
return false;
}
public override bool TrySetMember(SetMemberBinder binder, dynamic value)
{
_values[binder.Name] = value;
return true;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return _values.Keys;
}
public dynamic this[string key]
{
get { return _values[key]; }
set { _values[key] = value; }
}
}
//
// Code below from CodeProject
// SharpDevelop shits itself at the amount of warnings this yields.
// http://www.codeproject.com/Articles/42929/C-Exposer-an-evil-DynamicObject
//
public interface IConvertTo<T>
{
T Convert();
}
public interface IObjectWithType
{
Type Type { get; set; }
}
public class Exposer<T> : DynamicObject, IObjectWithType, IConvertTo<T>
{
public T Object { get; set; }
public Type Type { get; set; }
static Dictionary<string, Func<T, object[], object>> _methods =
new Dictionary<string, Func<T, object[], object>>();
static Dictionary<string, Func<T, object>> _getters =
new Dictionary<string, Func<T, object>>();
static Dictionary<string, Action<T, object>> _setters =
new Dictionary<string, Action<T, object>>();
static MethodInfo _doConvert = typeof(Exposer<T>).GetMethod
("DoConvert", BindingFlags.NonPublic | BindingFlags.Static);
public Exposer(T obj)
{
this.Object = obj;
this.Type = obj.GetType();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var key = binder.Name;
Func<T, object> getter = null;
if (_getters.ContainsKey(key))
{
getter = _getters[key];
}
else
{
IEnumerable<MemberInfo> members = this.Type.GetMembers(
BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.GetProperty |
BindingFlags.GetField);
members = from mem in members
where mem.Name == key
select mem;
var member = members.FirstOrDefault();
if(member != null) {
getter = BuildGetter(member);
_getters.Add(key, getter);
}
}
if (getter != null)
{
result = Wrap(getter(this.Object));
return true;
}
else
return base.TryGetMember(binder, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
var key = binder.Name;
Action<T, object> setter = null;
if (_setters.ContainsKey(key))
{
setter = _setters[key];
}
else
{
IEnumerable<MemberInfo> members = this.Type.GetMembers(
BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.SetProperty |
BindingFlags.SetField);
members = from mem in members
where mem.Name == key
select mem;
var member = members.FirstOrDefault();
if (member != null)
{
setter = BuildSetter(member);
_setters.Add(key, setter);
}
}
if (setter != null)
{
setter(this.Object, value);
return true;
}
else
return base.TrySetMember(binder, value);
}
public override bool TryInvokeMember
(InvokeMemberBinder binder, object[] args, out object result)
{
Func<T, object[], object> func = null;
var key = MakeKey(binder, args);
if (_methods.ContainsKey(key))
func = _methods[key];
else
{
var argTypes = args.Select(arg => arg is IObjectWithType ?
(arg as IObjectWithType).Type : arg.GetType()).ToArray();
IEnumerable<MethodInfo> methods = this.Type.GetMethods
(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
methods = from method in methods
where method.Name == binder.Name && ArgsMatch(method, argTypes)
select method;
var info = methods.FirstOrDefault();
if (info != null)
{
var paramTypes = info.GetParameters().Select
(p => p.ParameterType).ToArray();
var target = Expression.Parameter(this.Type, "obj");
var param = Expression.Parameter(typeof(object[]), "args");
var call = Expression.Call(target, info,
paramTypes.Select((p, i) =>
BuildConvertExpression(Expression.ArrayAccess
(param, Expression.Constant(i)), p)));
func = Expression.Lambda<Func<T, object[], object>>(
Expression.Convert(call, typeof(object)),
target, param).Compile();
}
_methods.Add(key, func);
}
if (func != null)
{
var res = func(this.Object, args);
result = Wrap(res);
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
public override bool TryConvert(ConvertBinder binder, out object result)
{
if (binder.Type.IsAssignableFrom(this.Type))
{
result = this.Object;
return true;
}
return base.TryConvert(binder, out result);
}
#region Builders
#region Getter
private Func<T, object> BuildGetter(MemberInfo member)
{
switch (member.MemberType)
{
case MemberTypes.Field:
return BuildFieldGetter(member as FieldInfo);
case MemberTypes.Property:
return BuildPropertyGetter(member as PropertyInfo);
default:
//Returning null effectively marks this as not supported,
//since the getter will be null as binding exception will be thrown
return null;
}
}
private Func<T, object> BuildFieldGetter(FieldInfo fieldInfo)
{
var param = Expression.Parameter(this.Type, "obj");
var lambda = Expression.Lambda<Func<T, object>>(
Expression.Field(param, fieldInfo),
param);
return lambda.Compile();
}
private Func<T, object> BuildPropertyGetter(PropertyInfo propertyInfo)
{
var param = Expression.Parameter(this.Type, "obj");
var lambda = Expression.Lambda<Func<T, object>>(
Expression.Property(param, propertyInfo),
param);
return lambda.Compile();
}
#endregion
#region Setter
private Action<T, object> BuildSetter(MemberInfo member)
{
switch (member.MemberType)
{
case MemberTypes.Field:
return BuildFieldSetter(member as FieldInfo);
case MemberTypes.Property:
return BuildPropertySetter(member as PropertyInfo);
default:
//Returning null effectively marks this as not supported,
//since the setter will be null as binding exception will be thrown
return null;
}
}
private Action<T, object> BuildFieldSetter(FieldInfo fieldInfo)
{
var param = Expression.Parameter(this.Type, "obj");
var value = Expression.Parameter(typeof(object), "val");
var lambda = Expression.Lambda<Action<T, object>>(
Expression.Assign(
Expression.Field(param, fieldInfo),
Expression.Convert(value, fieldInfo.FieldType)),
param, value);
return lambda.Compile();
}
private Action<T, object> BuildPropertySetter(PropertyInfo propertyInfo)
{
var param = Expression.Parameter(this.Type, "obj");
var value = Expression.Parameter(typeof(object), "val");
var lambda = Expression.Lambda<Action<T, object>>(
Expression.Assign(
Expression.Property(param, propertyInfo),
Expression.Convert(value, propertyInfo.PropertyType)),
param, value);
return lambda.Compile();
}
#endregion
#region Convert
public Expression BuildConvertExpression(Expression target, Type type)
{
if (type == typeof(object))
return target;
return Expression.Call(_doConvert.MakeGenericMethod(type), target);
}
static R DoConvert<R>(object i)
{
if (i is IConvertTo<R>)
{
return (i as IConvertTo<R>).Convert();
}
else
{
return (R)i;
}
}
#endregion
#endregion
#region Helpers
private static object Wrap(object res)
{
if (res == null)
return null;
var type = res.GetType();
if (type.IsPrimitive)
return res;
var expType = typeof(Exposer<>).MakeGenericType(type);
return Activator.CreateInstance(expType, res);
}
private static string MakeKey(InvokeMemberBinder binder, object[] args)
{
var ret = new StringBuilder();
ret.Append(binder.Name);
foreach (var arg in args)
ret.Append(arg.GetType().Name);
return ret.ToString();
}
private static bool ArgsMatch(MethodInfo info, Type[] argTypes)
{
return info.GetParameters()
.Select((p, i) => p.ParameterType.IsAssignableFrom(argTypes[i]))
.All(b => b);
}
#endregion
#region IConvertTo<T> Members
public T Convert()
{
return this.Object;
}
#endregion
}
public static class XmlExtensions
{
public static Exposer<T> Expose<T>(this T target)
{
return new Exposer<T>(target);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment