Skip to content

Instantly share code, notes, and snippets.

@lbargaoanu
Created March 4, 2017 17:48
Show Gist options
  • Save lbargaoanu/32a7586583a6864ae9308161b1c998db to your computer and use it in GitHub Desktop.
Save lbargaoanu/32a7586583a6864ae9308161b1c998db to your computer and use it in GitHub Desktop.
using System;
using System.IO;
using System.Xml;
using System.Reflection;
using System.Diagnostics;
using System.Globalization;
using System.ComponentModel;
using System.Collections.Generic;
public static class ConfigurationManager
{
private static readonly Dictionary<Type, Defaults> defaults = new Dictionary<Type, Defaults>();
private const string FileName = "Domain.config";
static ConfigurationManager()
{
XmlDocument document = new XmlDocument();
document.Load(FileName);
foreach(XmlElement typeElement in document.SelectNodes("types/type"))
{
string typeName = typeElement.Attributes["name"].Value;
string assemblyName = typeElement.Attributes["assembly"].Value;
Type type = Type.GetType(typeName + "," + assemblyName, true);
defaults.Add(type, new Defaults(type, typeElement));
}
}
public static void Initialize(object obj)
{
if(obj == null)
{
throw new ArgumentNullException("obj");
}
Defaults def;
if(defaults.TryGetValue(obj.GetType(), out def))
{
def.Set(obj);
}
}
struct Default
{
readonly FieldInfo fieldInfo;
readonly object value;
public Default(FieldInfo fieldInfo, string strValue)
{
TypeConverter converter = TypeDescriptor.GetConverter(fieldInfo.FieldType);
if(converter.GetType() == typeof(TypeConverter))
{
throw new ApplicationException("Cannot find converter for type " + fieldInfo.FieldType.AssemblyQualifiedName);
}
try
{
value = converter.ConvertFromString(null, CultureInfo.InvariantCulture, strValue);
}
catch(Exception ex)
{
throw new ApplicationException("Cannot initialize field \"" + fieldInfo.Name + "\" of type " + fieldInfo.ReflectedType.AssemblyQualifiedName, ex);
}
this.fieldInfo = fieldInfo;
}
public void Set(object obj)
{
fieldInfo.SetValue(obj, value);
}
public void Set(TypedReference obj)
{
fieldInfo.SetValueDirect(obj, value);
}
}
struct Defaults
{
// the chain of parents that got us here
readonly FieldInfo[] parentFields;
// the primitive fields of our type, ThisField.FieldType
readonly Default[] defaults;
// the non-primitive fields of our type, ThisField.FieldType
readonly Defaults[] nestedDefaults;
public Defaults(Type type, XmlElement typeElement, FieldInfo[] parentFields)
{
this.parentFields = parentFields;
List<Default> defaultsList = new List<Default>(typeElement.ChildNodes.Count);
List<Defaults> nestedDefaultsList = new List<Defaults>();
foreach(XmlElement fieldElement in typeElement.SelectNodes("field"))
{
string name = fieldElement.Attributes["name"].Value;
FieldInfo fieldInfo = type.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if(fieldInfo == null)
{
throw new ApplicationException("Cannot find field " + name + " of type " + type.AssemblyQualifiedName);
}
if(fieldElement.HasChildNodes)
{
nestedDefaultsList.Add(new Defaults(fieldInfo.FieldType, fieldElement, AddThisParent(parentFields, fieldInfo)));
}
else
{
string strValue = fieldElement.Attributes["value"].Value;
defaultsList.Add(new Default(fieldInfo, strValue));
}
}
defaults = defaultsList.ToArray();
nestedDefaults = (nestedDefaultsList.Count > 0) ? nestedDefaultsList.ToArray() : null;
}
public Defaults(Type type, XmlElement typeElement) : this(type, typeElement, null)
{
}
private static FieldInfo[] AddThisParent(FieldInfo[] parentFields, FieldInfo fieldInfo)
{
FieldInfo[] newParentFields;
if(parentFields == null)
{
newParentFields = new FieldInfo[] { fieldInfo };
}
else
{
newParentFields = new FieldInfo[parentFields.Length + 1];
Array.Copy(parentFields, newParentFields, parentFields.Length);
newParentFields[newParentFields.Length - 1] = fieldInfo;
}
return newParentFields;
}
// this object handles this FieldInfo and its children
private FieldInfo ThisField
{
get
{
return parentFields[parentFields.Length - 1];
}
}
public void Set(object obj)
{
if(parentFields != null)
{
// we must take extra care for structs; we cannot use SetField with an object parameter because we box and change that boxed instance
// instead we must work with TypedReference-s and pass the root object along recursively
if(ThisField.FieldType.IsValueType)
{
TypedReference referenceToSet = TypedReference.MakeTypedReference(obj, parentFields);
foreach(Default def in defaults)
{
def.Set(referenceToSet);
}
if(nestedDefaults != null)
{
object parent = null;
foreach(Defaults def in nestedDefaults)
{
if(def.ThisField.FieldType.IsValueType)
{
// cannot update obj, the chaining is done through the parent fields
def.Set(obj);
}
else
{
if(parent == null)
{
// if we are a "nested" object we need to update the object to set; obj is actually our parent now; make it point to us
parent = ThisField.GetValue(obj);
}
def.Set(parent);
}
}
}
return;
}
// if we are a "nested" object we need to update the object to set; obj is actually our parent now; make it point to us
obj = ThisField.GetValue(obj);
}
if (obj == null)
{
throw new ArgumentNullException("Field: " + ThisField.ToString());
}
foreach (Default def in defaults)
{
def.Set(obj);
}
if(nestedDefaults != null)
{
foreach(Defaults def in nestedDefaults)
{
def.Set(obj);
}
}
}
}
}
//namespace Program
//{
// class Program
// {
// static void Main(string[] args)
// {
// TestThread th = new TestThread("dedw");
// }
// }
// struct StrNested
// {
// int z;
// }
// class Base
// {
// string _string = null;
// }
// struct Str
// {
// public Str(int i)
// {
// zzz = new StrNested();
// b = new Base();
// x = y = 9;
// }
// StrNested zzz;
// int x;
// int y;
// Base b;
// }
// public class TestThread
// {
// Str structura = new Str(1);
// int _int = 0;
// internal float _float = 0;
// protected string _string = null;
// public DateTime _dateTime = DateTime.MinValue;
// TestThread _testThread = null;
// Base threadB = new Base();
// public TestThread(string name)
// {
// ConfigurationManager.Initialize(this);
// }
// }
//}
//
//<?xml version="1.0" encoding="utf-8" ?>
//<types>
// <type name="Program.TestThread" assembly="Console2005">
// <field name="threadB">
// <field name="_string" value="class in class" />
// </field>
// <field name="_int" value="-1" />
// <field name="_float" value="23.45" />
// <field name="_string" value="iaca o venit" />
// <field name="_dateTime" value="3/31/2006 10:59:26 AM" />
// <field name="structura">
// <field name="x" value="65" />
// <field name="y" value="89" />
// <field name="zzz">
// <field name="z" value="17" />
// </field>
// <field name="b">
// <field name="_string" value="class in struct" />
// </field>
// </field>
// </type>
//</types>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment