/* * XmlStaticMemberSerializer.cs * Copyright (c) 2009 kbinani * * This file is part of Boare.Lib.AppUtil. * * Boare.Lib.AppUtil is free software; you can redistribute it and/or * modify it under the terms of the BSD License. * * Boare.Lib.AppUtil is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Xml.Serialization; using Microsoft.CSharp; namespace Boare.Lib.AppUtil { /// /// クラスのstaticメンバーのxmlシリアライズ/デシリアライズを行うclass /// public class XmlStaticMemberSerializer { /// /// ターゲットとなるクラスから,シリアライズするメンバーを抽出する時に使用 /// private class MemberEntry { /// /// プロパティ/フィールドの名前 /// public string Name; /// /// プロパティ/フィールドの型 /// public Type Type; /// /// プロパティ/フィールドのデフォルト値 /// public object Default; public MemberEntry( string name, Type type, object default_ ) { Name = name; Type = type; Default = default_; } } /// /// シリアライズする対象の型.staticメンバーだけなので,インスタンスではなく型を保持 /// private Type m_item; /// /// シリアライズ/デシリアライズするための内部型 /// private Type m_config_type = null; /// /// m_config_typeで初期化されたシリアライザ /// private XmlSerializer m_xs = null; private XmlStaticMemberSerializer() { } /// /// 指定された型をシリアライズするための初期化を行います /// /// public XmlStaticMemberSerializer( Type item ) { m_item = item; } /// /// シリアライズを行い,ストリームに書き込みます /// /// public void Serialize( Stream stream ) { if ( m_config_type == null ) { GenerateConfigType(); } ConstructorInfo ci = m_config_type.GetConstructor( new Type[]{} ); object config = ci.Invoke( new object[]{} ); foreach ( FieldInfo target in m_config_type.GetFields() ) { foreach ( PropertyInfo pi in m_item.GetProperties( BindingFlags.Public | BindingFlags.Static ) ) { if ( target.Name == pi.Name && target.FieldType.Equals( pi.PropertyType ) && pi.CanRead && pi.CanWrite ) { target.SetValue( config, pi.GetValue( m_item, new object[] { } ) ); break; } } foreach ( FieldInfo fi in m_item.GetFields( BindingFlags.Public | BindingFlags.Static ) ) { if ( target.Name == fi.Name && target.FieldType.Equals( fi.FieldType ) ) { target.SetValue( config, fi.GetValue( m_item ) ); break; } } } m_xs.Serialize( stream, config ); } /// /// 指定したストリームを使って,デシリアライズを行います /// /// public void Deserialize( Stream stream ) { if ( m_config_type == null ) { GenerateConfigType(); } object config = m_xs.Deserialize( stream ); if ( config == null ) { throw new ApplicationException( "failed serializing internal config object" ); } foreach ( FieldInfo target in m_config_type.GetFields() ) { foreach ( PropertyInfo pi in m_item.GetProperties( BindingFlags.Public | BindingFlags.Static ) ) { if ( target.Name == pi.Name && target.FieldType.Equals( pi.PropertyType ) && pi.CanRead && pi.CanWrite ) { pi.SetValue( m_item, target.GetValue( config ), new object[] { } ); break; } } foreach ( FieldInfo fi in m_item.GetFields( BindingFlags.Public | BindingFlags.Static ) ) { if ( target.Name == fi.Name && target.FieldType.Equals( fi.FieldType ) ) { fi.SetValue( m_item, target.GetValue( config ) ); break; } } } } /// /// シリアライズ用の内部型をコンパイルし,m_xsが使用できるようにします /// private void GenerateConfigType() { List config_names = CollectScriptConfigEntries( m_item ); string code = GenerateClassCodeForXmlSerialization( config_names, m_item ); CSharpCodeProvider provider = new CSharpCodeProvider(); CompilerParameters parameters = new CompilerParameters(); parameters.ReferencedAssemblies.Add( "System.Windows.Forms.dll" ); parameters.ReferencedAssemblies.Add( "System.dll" ); parameters.ReferencedAssemblies.Add( "System.Drawing.dll" ); parameters.ReferencedAssemblies.Add( "System.Xml.dll" ); parameters.GenerateInMemory = true; parameters.GenerateExecutable = false; parameters.IncludeDebugInformation = true; CompilerResults results = provider.CompileAssemblyFromSource( parameters, code ); Assembly asm = results.CompiledAssembly; if ( asm.GetTypes().Length <= 0 ) { m_config_type = null; m_xs = null; throw new ApplicationException( "failed generating internal xml serizlizer" ); } else { m_config_type = (asm.GetTypes())[0]; m_xs = new XmlSerializer( m_config_type ); } } /// /// 設定ファイルから読込むための型情報を蒐集 /// /// /// private static List CollectScriptConfigEntries( Type item ) { List config_names = new List(); foreach ( PropertyInfo pi in item.GetProperties( BindingFlags.Static | BindingFlags.Public ) ) { object[] attrs = pi.GetCustomAttributes( true ); foreach ( object obj in attrs ) { if ( obj.GetType().Equals( typeof( System.Xml.Serialization.XmlIgnoreAttribute ) ) ) { continue; } } if ( pi.CanRead && pi.CanWrite ) { config_names.Add( new MemberEntry( pi.Name, pi.PropertyType, pi.GetValue( item, new object[] { } ) ) ); } } foreach ( FieldInfo fi in item.GetFields( BindingFlags.Static | BindingFlags.Public ) ) { object[] attrs = fi.GetCustomAttributes( true ); foreach ( object obj in attrs ) { if ( obj.GetType().Equals( typeof( System.Xml.Serialization.XmlIgnoreAttribute ) ) ) { continue; } } config_names.Add( new MemberEntry( fi.Name, fi.FieldType, fi.GetValue( item ) ) ); } return config_names; } /// /// 指定した型から、Reflectionを使ってxmlシリアライズ用のクラスをコンパイルするためのC#コードを作成します /// /// /// private static string GenerateClassCodeForXmlSerialization( List config_names, Type item ) { // XmlSerialization用の型を作成 string code = ""; code += "using System;\n"; code += "namespace Boare.Lib.AppUtil{\n"; code += " public class StaticMembersOf" + item.Name + "{\n"; foreach ( MemberEntry entry in config_names ) { code += " public " + entry.Type.ToString() + " " + entry.Name + ";\n"; } code += " }\n"; code += "}\n"; return code; } } }