OSDN Git Service

#30244 Visual Studio 2012 Express for Windows Desktop, StyleCop 4.7, WiX 3.6 に合わせたソース...
[wptscs/wpts.git] / Wptscs / Models / Config.cs
index 10a72ac..53e745d 100644 (file)
-// ================================================================================================\r
-// <summary>\r
-//      アプリケーションの設定を保持するクラスソース</summary>\r
-//\r
-// <copyright file="Config.cs" company="honeplusのメモ帳">\r
-//      Copyright (C) 2010 Honeplus. All rights reserved.</copyright>\r
-// <author>\r
-//      Honeplus</author>\r
-// ================================================================================================\r
-\r
-namespace Honememo.Wptscs.Models\r
-{\r
-    using System;\r
-    using System.Collections.Generic;\r
-    using System.IO;\r
-    using System.Windows.Forms;\r
-    using System.Xml;\r
-    using System.Xml.Serialization;\r
-    using Honememo.Utilities;\r
-    using Honememo.Wptscs.Logics;\r
-    using Honememo.Wptscs.Properties;\r
-\r
-    /// <summary>\r
-    /// アプリケーションの設定を保持するクラスです。\r
-    /// </summary>\r
-    public class Config : IXmlSerializable\r
-    {\r
-        #region 静的変数\r
-\r
-        /// <summary>\r
-        /// アプリケーション内でのインスタンス保持変数。\r
-        /// </summary>\r
-        private static IDictionary<string, Config> configs = new Dictionary<string, Config>();\r
-\r
-        #endregion\r
-\r
-        #region private変数\r
-\r
-        /// <summary>\r
-        /// 言語に関する情報。\r
-        /// </summary>\r
-        private IList<Language> languages = new List<Language>();\r
-\r
-        /// <summary>\r
-        /// ウェブサイトの情報。\r
-        /// </summary>\r
-        private IList<Website> websites = new List<Website>();\r
-\r
-        /// <summary>\r
-        /// 言語間の項目の対訳表。\r
-        /// </summary>\r
-        private IList<TranslationDictionary> itemTables = new List<TranslationDictionary>();\r
-\r
-        /// <summary>\r
-        /// 言語間の見出しの対訳表。\r
-        /// </summary>\r
-        private TranslationTable headingTable = new TranslationTable();\r
-\r
-        #endregion\r
-\r
-        #region コンストラクタ\r
-\r
-        /// <summary>\r
-        /// コンストラクタ。\r
-        /// </summary>\r
-        /// <remarks>通常は<see cref="GetInstance(string)"/>を使用する。</remarks>\r
-        protected Config()\r
-        {\r
-        }\r
-\r
-        #endregion\r
-        \r
-        #region プロパティ\r
-\r
-        /// <summary>\r
-        /// 翻訳支援処理で使用するロジッククラス名。\r
-        /// </summary>\r
-        public Type Translator\r
-        {\r
-            get;\r
-            set;\r
-        }\r
-\r
-        /// <summary>\r
-        /// ウェブサイトの情報。\r
-        /// </summary>\r
-        /// <remarks>空でもオブジェクトは存在。</remarks>\r
-        public IList<Website> Websites\r
-        {\r
-            get\r
-            {\r
-                return this.websites;\r
-            }\r
-\r
-            set\r
-            {\r
-                // ※必須な情報が設定されていない場合、例外を返す\r
-                this.websites = Validate.NotNull(value, "websites");\r
-            }\r
-        }\r
-\r
-        /// <summary>\r
-        /// 言語間の項目の対訳表。\r
-        /// </summary>\r
-        /// <remarks>空でもオブジェクトは存在。</remarks>\r
-        public IList<TranslationDictionary> ItemTables\r
-        {\r
-            get\r
-            {\r
-                return this.itemTables;\r
-            }\r
-\r
-            set\r
-            {\r
-                // ※必須な情報が設定されていない場合、例外を返す\r
-                this.itemTables = Validate.NotNull(value, "itemTables");\r
-            }\r
-        }\r
-\r
-        /// <summary>\r
-        /// 言語間の見出しの対訳表。\r
-        /// </summary>\r
-        /// <remarks>空でもオブジェクトは存在。</remarks>\r
-        public TranslationTable HeadingTable\r
-        {\r
-            get\r
-            {\r
-                return this.headingTable;\r
-            }\r
-\r
-            set\r
-            {\r
-                // ※必須な情報が設定されていない場合、例外を返す\r
-                this.headingTable = Validate.NotNull(value, "headingTable");\r
-            }\r
-        }\r
-\r
-        #endregion\r
-\r
-        #region 静的メソッド\r
-\r
-        /// <summary>\r
-        /// アプリケーションの設定を取得する。\r
-        /// ユーザーごとの設定ファイルがあればその内容を、\r
-        /// なければアプリケーション標準の設定ファイルの内容を\r
-        /// 読み込んで、インスタンスを作成する。\r
-        /// </summary>\r
-        /// <param name="file">設定ファイル名。</param>\r
-        /// <returns>作成した/既に存在するインスタンス。</returns>\r
-        public static Config GetInstance(string file)\r
-        {\r
-            // シングルトンとするため、処理をロック\r
-            lock (configs)\r
-            {\r
-                // 既に作成済みのインスタンスがあればその値を使用\r
-                // (設定ファイルのタイムスタンプとか確認して再読み込みした方がよい?)\r
-                if (Config.configs.ContainsKey(file))\r
-                {\r
-                    return Config.configs[file];\r
-                }\r
-\r
-                // 無い場合はユーザーごと・または初期設定用の設定ファイルを読み込み\r
-                string path = FormUtils.SearchUserAppData(file, Settings.Default.ConfigurationCompatible);\r
-                if (String.IsNullOrEmpty(path))\r
-                {\r
-                    // どこにも無い場合は例外を投げる\r
-                    // (空でnewしてもよいが、ユーザーが勘違いすると思うので。)\r
-                    throw new FileNotFoundException(file + " is not found");\r
-                }\r
-\r
-                // 設定ファイルを読み込み\r
-                System.Diagnostics.Debug.WriteLine("Config.GetInstance > " + path + " を読み込み");\r
-                using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read))\r
-                {\r
-                    Config.configs[file] = new XmlSerializer(typeof(Config)).Deserialize(stream) as Config;\r
-                }\r
-            }\r
-\r
-            return Config.configs[file];\r
-        }\r
-\r
-        #endregion\r
-\r
-        #region インスタンスメソッド\r
-\r
-        /// <summary>\r
-        /// 設定をユーザーごとの設定ファイルに書き出し。\r
-        /// </summary>\r
-        /// <param name="file">設定ファイル名。</param>\r
-        public void Save(string file)\r
-        {\r
-            // ファイル出力のため、競合しないよう一応ロック\r
-            lock (Config.configs)\r
-            {\r
-                // 最初にディレクトリの有無を確認し作成\r
-                string path = Application.UserAppDataPath;\r
-                if (!Directory.Exists(path))\r
-                {\r
-                    Directory.CreateDirectory(path);\r
-                }\r
-\r
-                // 設定ファイルを出力\r
-                using (Stream stream = new FileStream(\r
-                    Path.Combine(path, file),\r
-                    FileMode.Create))\r
-                {\r
-                    new XmlSerializer(typeof(Config)).Serialize(stream, this);\r
-                }\r
-            }\r
-        }\r
-        \r
-        #endregion\r
-\r
-        #region 設定値取得用インスタンスメソッド\r
-\r
-        /// <summary>\r
-        /// 設定から、現在の処理対象・指定された言語のウェブサイトを取得する。\r
-        /// </summary>\r
-        /// <param name="lang">言語コード。</param>\r
-        /// <returns>ウェブサイトの情報。存在しない場合は<c>null</c>返す。</returns>\r
-        public Website GetWebsite(string lang)\r
-        {\r
-            // 設定が存在すれば取得した値を返す\r
-            foreach (Website s in this.Websites)\r
-            {\r
-                if (s.Language.Code == lang)\r
-                {\r
-                    return s;\r
-                }\r
-            }\r
-\r
-            // 存在しない場合、nullを返す\r
-            // ※ こっちがNeedCreateじゃないのは、何をnewすればいいのか判らないため\r
-            return null;\r
-        }\r
-        \r
-        /// <summary>\r
-        /// 設定から、現在の処理対象・指定された言語の対訳表(項目)を取得する。\r
-        /// </summary>\r
-        /// <param name="from">翻訳元言語。</param>\r
-        /// <param name="to">翻訳先言語。</param>\r
-        /// <returns>対訳表の情報。存在しない場合は新たに作成した対訳表を返す。</returns>\r
-        public TranslationDictionary GetItemTableNeedCreate(string from, string to)\r
-        {\r
-            // オブジェクトに用意されている共通メソッドをコール\r
-            return TranslationDictionary.GetDictionaryNeedCreate(this.ItemTables, from, to);\r
-        }\r
-\r
-        #endregion\r
-\r
-        #region XMLシリアライズ用メソッド\r
-\r
-        /// <summary>\r
-        /// シリアライズするXMLのスキーマ定義を返す。\r
-        /// </summary>\r
-        /// <returns>XML表現を記述するXmlSchema。</returns>\r
-        public System.Xml.Schema.XmlSchema GetSchema()\r
-        {\r
-            return null;\r
-        }\r
-\r
-        /// <summary>\r
-        /// XMLからオブジェクトを読み込む。\r
-        /// </summary>\r
-        /// <param name="reader">読込元のXmlReader</param>\r
-        public void ReadXml(XmlReader reader)\r
-        {\r
-            XmlDocument xml = new XmlDocument();\r
-            xml.Load(reader);\r
-\r
-            // ルートエレメントを取得\r
-            // ※ 以下、基本的に無かったらNGの部分はいちいちチェックしない。例外飛ばす\r
-            XmlElement rootElement = xml.DocumentElement;\r
-\r
-            // ロジッククラス\r
-            this.Translator = this.ParseTranslator(rootElement.SelectSingleNode("Translator").InnerText);\r
-\r
-            // Webサイト\r
-            foreach (XmlNode siteNode in rootElement.SelectSingleNode("Websites").ChildNodes)\r
-            {\r
-                // ノードに指定された内容に応じたインスタンスを取得する\r
-                this.Websites.Add(this.ParseWebsite(siteNode, reader.Settings));\r
-            }\r
-\r
-            // 項目の対訳表\r
-            XmlSerializer serializer = new XmlSerializer(typeof(TranslationDictionary), new XmlRootAttribute("ItemTable"));\r
-            foreach (XmlNode itemNode in rootElement.SelectSingleNode("ItemTables").ChildNodes)\r
-            {\r
-                using (XmlReader r = XmlReader.Create(new StringReader(itemNode.OuterXml), reader.Settings))\r
-                {\r
-                    this.ItemTables.Add(serializer.Deserialize(r) as TranslationDictionary);\r
-                }\r
-            }\r
-\r
-            // 見出しの対訳表\r
-            using (XmlReader r = XmlReader.Create(\r
-                new StringReader(rootElement.SelectSingleNode("HeadingTable").OuterXml),\r
-                reader.Settings))\r
-            {\r
-                this.HeadingTable = new XmlSerializer(typeof(TranslationTable), new XmlRootAttribute("HeadingTable"))\r
-                    .Deserialize(r) as TranslationTable;\r
-            }\r
-        }\r
-\r
-        /// <summary>\r
-        /// オブジェクトをXMLに出力する。\r
-        /// </summary>\r
-        /// <param name="writer">出力先のXmlWriter</param>\r
-        public void WriteXml(XmlWriter writer)\r
-        {\r
-            // ロジッククラス\r
-            string translator = this.Translator.FullName;\r
-            if (translator.StartsWith(typeof(Translator).Namespace))\r
-            {\r
-                // 自前のエンジンの場合、クラス名だけを出力\r
-                translator = this.Translator.Name;\r
-            }\r
-\r
-            writer.WriteElementString("Translator", translator);\r
-\r
-            // 各処理モードのWebサイト\r
-            writer.WriteStartElement("Websites");\r
-            foreach (Website site in this.Websites)\r
-            {\r
-                // 通常はサイトのパッケージ名も含めたフル名を要素名とする\r
-                string siteName = site.GetType().FullName;\r
-                if (siteName.StartsWith(typeof(Website).Namespace))\r
-                {\r
-                    // 自前のサイトの場合、クラス名だけを出力\r
-                    siteName = site.GetType().Name;\r
-                }\r
-\r
-                new XmlSerializer(site.GetType(), new XmlRootAttribute(siteName)).Serialize(writer, site);\r
-            }\r
-\r
-            writer.WriteEndElement();\r
-\r
-            // 項目の対訳表\r
-            XmlSerializer serializer = new XmlSerializer(typeof(TranslationDictionary), new XmlRootAttribute("ItemTable"));\r
-            writer.WriteStartElement("ItemTables");\r
-            foreach (TranslationDictionary trans in this.ItemTables)\r
-            {\r
-                serializer.Serialize(writer, trans);\r
-            }\r
-\r
-            writer.WriteEndElement();\r
-\r
-            // 見出しの対訳表\r
-            new XmlSerializer(this.HeadingTable.GetType(), new XmlRootAttribute("HeadingTable"))\r
-                .Serialize(writer, this.HeadingTable);\r
-        }\r
-\r
-        /// <summary>\r
-        /// 指定されたXML値からTranslatorのクラスを取得するる。\r
-        /// </summary>\r
-        /// <param name="name">XMLのクラス名情報。</param>\r
-        /// <returns>Translatorクラス。</returns>\r
-        /// <remarks>クラスは動的に判定する。クラスが存在しない場合などは随時状況に応じた例外を投げる。</remarks>\r
-        private Type ParseTranslator(string name)\r
-        {\r
-            // Translateと同じパッケージに指定された名前のクラスがあるかを探す\r
-            Type type = Type.GetType(typeof(Translator).Namespace + "." + name, false, true);\r
-            if (type == null)\r
-            {\r
-                // 存在しない場合、そのままの名前でクラスを探索、無ければ例外スロー\r
-                type = Type.GetType(name, true, true);\r
-            }\r
-\r
-            return type;\r
-        }\r
-\r
-        /// <summary>\r
-        /// XMLノードからWebSiteインスタンスをデシリアライズする。\r
-        /// </summary>\r
-        /// <param name="node">WebSiteをシリアライズしたノード。</param>\r
-        /// <param name="setting">XML読み込み時の設定。</param>\r
-        /// <returns>デシリアライズしたインスタンス。</returns>\r
-        /// <remarks>クラスはノード名から動的に判定する。クラスが存在しない場合などは随時状況に応じた例外を投げる。</remarks>\r
-        private Website ParseWebsite(XmlNode node, XmlReaderSettings setting)\r
-        {\r
-            // WebSiteと同じパッケージに指定された名前のクラスがあるかを探す\r
-            Type type = Type.GetType(typeof(Website).Namespace + "." + node.Name, false, true);\r
-            if (type == null)\r
-            {\r
-                // 存在しない場合、そのままの名前でクラスを探索、無ければ例外スロー\r
-                type = Type.GetType(node.Name, true, true);\r
-            }\r
-\r
-            using (XmlReader r = XmlReader.Create(new StringReader(node.OuterXml), setting))\r
-            {\r
-                return new XmlSerializer(type).Deserialize(r) as Website;\r
-            }\r
-        }\r
-\r
-        #endregion\r
-    }\r
-}\r
+// ================================================================================================
+// <summary>
+//      アプリケーションの設定を保持するクラスソース</summary>
+//
+// <copyright file="Config.cs" company="honeplusのメモ帳">
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
+// <author>
+//      Honeplus</author>
+// ================================================================================================
+
+namespace Honememo.Wptscs.Models
+{
+    using System;
+    using System.Collections.Generic;
+    using System.IO;
+    using System.Windows.Forms;
+    using System.Xml;
+    using System.Xml.Serialization;
+    using Honememo.Utilities;
+    using Honememo.Wptscs.Logics;
+    using Honememo.Wptscs.Properties;
+    using Honememo.Wptscs.Utilities;
+    using Honememo.Wptscs.Websites;
+
+    /// <summary>
+    /// アプリケーションの設定を保持するクラスです。
+    /// </summary>
+    public class Config : IXmlSerializable
+    {
+        #region private変数
+
+        /// <summary>
+        /// ウェブサイトの情報。
+        /// </summary>
+        private IList<Website> websites = new List<Website>();
+
+        /// <summary>
+        /// 言語間の項目の対訳表。
+        /// </summary>
+        private IList<TranslationDictionary> itemTables = new List<TranslationDictionary>();
+
+        /// <summary>
+        /// 言語間の見出しの対訳表。
+        /// </summary>
+        private TranslationTable headingTable = new TranslationTable();
+
+        #endregion
+
+        #region コンストラクタ
+
+        /// <summary>
+        /// 空のインスタンスを生成する。
+        /// </summary>
+        /// <remarks>通常は<see cref="GetInstance(string)"/>で設定ファイルから読み込む。</remarks>
+        public Config()
+        {
+        }
+
+        #endregion
+        
+        #region プロパティ
+
+        /// <summary>
+        /// 設定ファイルと紐付いている場合のファイル名。
+        /// </summary>
+        /// <remarks>ファイルと紐付いていない場合は空。</remarks>
+        public string File
+        {
+            get;
+            set;
+        }
+
+        /// <summary>
+        /// 翻訳支援処理で使用するロジッククラス名。
+        /// </summary>
+        public Type Translator
+        {
+            get;
+            set;
+        }
+
+        /// <summary>
+        /// ウェブサイトの情報。
+        /// </summary>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
+        /// <remarks>空でもオブジェクトは存在。</remarks>
+        public IList<Website> Websites
+        {
+            get
+            {
+                return this.websites;
+            }
+
+            set
+            {
+                this.websites = Validate.NotNull(value);
+            }
+        }
+
+        /// <summary>
+        /// 言語間の項目の対訳表。
+        /// </summary>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
+        /// <remarks>空でもオブジェクトは存在。</remarks>
+        public IList<TranslationDictionary> ItemTables
+        {
+            get
+            {
+                return this.itemTables;
+            }
+
+            set
+            {
+                this.itemTables = Validate.NotNull(value);
+            }
+        }
+
+        /// <summary>
+        /// 言語間の見出しの対訳表。
+        /// </summary>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
+        /// <remarks>空でもオブジェクトは存在。</remarks>
+        public TranslationTable HeadingTable
+        {
+            get
+            {
+                return this.headingTable;
+            }
+
+            set
+            {
+                this.headingTable = Validate.NotNull(value);
+            }
+        }
+
+        #endregion
+
+        #region 静的メソッド
+
+        /// <summary>
+        /// アプリケーションの設定を取得する。
+        /// ユーザーごとの設定ファイルがあればその内容を、
+        /// なければアプリケーション標準の設定ファイルの内容を
+        /// 読み込んで、インスタンスを作成する。
+        /// </summary>
+        /// <param name="file">設定ファイル名。</param>
+        /// <returns>作成したインスタンス。</returns>
+        /// <exception cref="FileNotFoundException">指定されたファイルがどこにも存在しない場合。</exception>
+        public static Config GetInstance(string file)
+        {
+            // ユーザーごと・または初期設定用の設定ファイルを読み込み
+            string path = FormUtils.SearchUserAppData(file, Settings.Default.ConfigurationCompatible);
+            if (string.IsNullOrEmpty(path))
+            {
+                // どこにも無い場合は例外を投げる
+                throw new FileNotFoundException(file + " is not found");
+            }
+
+            // 設定ファイルを読み込み、読み込み元のファイル名は記録しておく
+            System.Diagnostics.Debug.WriteLine("Config.GetInstance > " + path + " を読み込み");
+            using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
+            {
+                Config config = new XmlSerializer(typeof(Config)).Deserialize(stream) as Config;
+                config.File = file;
+                return config;
+            }
+        }
+
+        #endregion
+
+        #region インスタンスメソッド
+
+        /// <summary>
+        /// 設定をユーザーごとの設定ファイルに書き出し。
+        /// </summary>
+        /// <exception cref="InvalidOperationException">
+        /// <see cref="File"/>にこのインスタンスと紐付くファイル名が指定されていない場合。
+        /// </exception>
+        public void Save()
+        {
+            // このインスタンスとファイルが紐付いていない場合、実行不可
+            if (string.IsNullOrWhiteSpace(this.File))
+            {
+                throw new InvalidOperationException("file is empty");
+            }
+
+            // 最初にディレクトリの有無を確認し作成
+            string path = Application.UserAppDataPath;
+            if (!Directory.Exists(path))
+            {
+                Directory.CreateDirectory(path);
+            }
+
+            // 設定ファイルを出力
+            using (Stream stream = new FileStream(Path.Combine(path, this.File), FileMode.Create))
+            {
+                new XmlSerializer(typeof(Config)).Serialize(stream, this);
+            }
+        }
+        
+        #endregion
+
+        #region 設定値取得用インスタンスメソッド
+
+        /// <summary>
+        /// 設定から、現在の処理対象・指定された言語のウェブサイトを取得する。
+        /// </summary>
+        /// <param name="lang">言語コード。</param>
+        /// <returns>ウェブサイトの情報。存在しない場合は<c>null</c>返す。</returns>
+        public Website GetWebsite(string lang)
+        {
+            // 設定が存在すれば取得した値を返す
+            foreach (Website s in this.Websites)
+            {
+                if (s.Language.Code == lang)
+                {
+                    return s;
+                }
+            }
+
+            // 存在しない場合、nullを返す
+            // ※ こっちがNeedCreateじゃないのは、何をnewすればいいのか判らないため
+            return null;
+        }
+        
+        /// <summary>
+        /// 設定から、現在の処理対象・指定された言語の対訳表(項目)を取得する。
+        /// </summary>
+        /// <param name="from">翻訳元言語。</param>
+        /// <param name="to">翻訳先言語。</param>
+        /// <returns>対訳表の情報。存在しない場合は新たに作成した対訳表を返す。</returns>
+        public TranslationDictionary GetItemTableNeedCreate(string from, string to)
+        {
+            // オブジェクトに用意されている共通メソッドをコール
+            return TranslationDictionary.GetDictionaryNeedCreate(this.ItemTables, from, to);
+        }
+
+        #endregion
+
+        #region XMLシリアライズ用メソッド
+
+        /// <summary>
+        /// シリアライズするXMLのスキーマ定義を返す。
+        /// </summary>
+        /// <returns>XML表現を記述する<see cref="System.Xml.Schema.XmlSchema"/>。</returns>
+        public System.Xml.Schema.XmlSchema GetSchema()
+        {
+            return null;
+        }
+
+        /// <summary>
+        /// XMLからオブジェクトを読み込む。
+        /// </summary>
+        /// <param name="reader">読込元の<see cref="XmlReader"/>。</param>
+        public void ReadXml(XmlReader reader)
+        {
+            XmlDocument xml = new XmlDocument();
+            xml.Load(reader);
+
+            // ルートエレメントを取得
+            // ※ 以下、基本的に無かったらNGの部分はいちいちチェックしない。例外飛ばす
+            XmlElement rootElement = xml.DocumentElement;
+
+            // ロジッククラス
+            this.Translator = this.ParseTranslator(rootElement.SelectSingleNode("Translator").InnerText);
+
+            // Webサイト
+            foreach (XmlNode siteNode in rootElement.SelectSingleNode("Websites").ChildNodes)
+            {
+                // ノードに指定された内容に応じたインスタンスを取得する
+                this.Websites.Add(this.ParseWebsite(siteNode, reader.Settings));
+            }
+
+            // 項目の対訳表
+            XmlSerializer serializer = new XmlSerializer(typeof(TranslationDictionary), new XmlRootAttribute("ItemTable"));
+            foreach (XmlNode itemNode in rootElement.SelectSingleNode("ItemTables").ChildNodes)
+            {
+                using (XmlReader r = XmlReader.Create(new StringReader(itemNode.OuterXml), reader.Settings))
+                {
+                    this.ItemTables.Add(serializer.Deserialize(r) as TranslationDictionary);
+                }
+            }
+
+            // 見出しの対訳表
+            using (XmlReader r = XmlReader.Create(
+                new StringReader(rootElement.SelectSingleNode("HeadingTable").OuterXml),
+                reader.Settings))
+            {
+                this.HeadingTable = new XmlSerializer(typeof(TranslationTable), new XmlRootAttribute("HeadingTable"))
+                    .Deserialize(r) as TranslationTable;
+            }
+        }
+
+        /// <summary>
+        /// オブジェクトをXMLに出力する。
+        /// </summary>
+        /// <param name="writer">出力先の<see cref="XmlWriter"/>。</param>
+        public void WriteXml(XmlWriter writer)
+        {
+            // ロジッククラス
+            string translator = this.Translator.FullName;
+            if (translator.StartsWith(typeof(Translator).Namespace))
+            {
+                // 自前のエンジンの場合、クラス名だけを出力
+                translator = this.Translator.Name;
+            }
+
+            writer.WriteElementString("Translator", translator);
+
+            // 各処理モードのWebサイト
+            writer.WriteStartElement("Websites");
+            foreach (Website site in this.Websites)
+            {
+                // 通常はサイトのパッケージ名も含めたフル名を要素名とする
+                string siteName = site.GetType().FullName;
+                if (siteName.StartsWith(typeof(Website).Namespace))
+                {
+                    // 自前のサイトの場合、クラス名だけを出力
+                    siteName = site.GetType().Name;
+                }
+
+                new XmlSerializer(site.GetType(), new XmlRootAttribute(siteName)).Serialize(writer, site);
+            }
+
+            writer.WriteEndElement();
+
+            // 項目の対訳表
+            XmlSerializer serializer = new XmlSerializer(typeof(TranslationDictionary), new XmlRootAttribute("ItemTable"));
+            writer.WriteStartElement("ItemTables");
+            foreach (TranslationDictionary trans in this.ItemTables)
+            {
+                serializer.Serialize(writer, trans);
+            }
+
+            writer.WriteEndElement();
+
+            // 見出しの対訳表
+            new XmlSerializer(this.HeadingTable.GetType(), new XmlRootAttribute("HeadingTable"))
+                .Serialize(writer, this.HeadingTable);
+        }
+
+        /// <summary>
+        /// 指定されたXML値から<see cref="Translator"/>のクラスを取得する。
+        /// </summary>
+        /// <param name="name">XMLのクラス名情報。</param>
+        /// <returns><see cref="Translator"/>クラス。</returns>
+        /// <remarks>クラスは動的に判定する。クラスが存在しない場合などは随時状況に応じた例外を投げる。</remarks>
+        private Type ParseTranslator(string name)
+        {
+            // Translateと同じパッケージに指定された名前のクラスがあるかを探す
+            Type type = Type.GetType(typeof(Translator).Namespace + "." + name, false, true);
+            if (type == null)
+            {
+                // 存在しない場合、そのままの名前でクラスを探索、無ければ例外スロー
+                type = Type.GetType(name, true, true);
+            }
+
+            return type;
+        }
+
+        /// <summary>
+        /// XMLノードから<see cref="Website"/>インスタンスをデシリアライズする。
+        /// </summary>
+        /// <param name="node"><see cref="Website"/>をシリアライズしたノード。</param>
+        /// <param name="setting">XML読み込み時の設定。</param>
+        /// <returns>デシリアライズしたインスタンス。</returns>
+        /// <remarks>クラスはノード名から動的に判定する。クラスが存在しない場合などは随時状況に応じた例外を投げる。</remarks>
+        private Website ParseWebsite(XmlNode node, XmlReaderSettings setting)
+        {
+            // WebSiteと同じパッケージに指定された名前のクラスがあるかを探す
+            Type type = Type.GetType(typeof(Website).Namespace + "." + node.Name, false, true);
+            if (type == null)
+            {
+                // 存在しない場合、そのままの名前でクラスを探索、無ければ例外スロー
+                type = Type.GetType(node.Name, true, true);
+            }
+
+            using (XmlReader r = XmlReader.Create(new StringReader(node.OuterXml), setting))
+            {
+                return new XmlSerializer(type).Deserialize(r) as Website;
+            }
+        }
+
+        #endregion
+    }
+}