OSDN Git Service

4a53d08560c4bde34e4f38e54ed2554d9669e2b3
[wptscs/wpts.git] / Wptscs / Models / Config.cs
1 // ================================================================================================
2 // <summary>
3 //      アプリケーションの設定を保持するクラスソース</summary>
4 //
5 // <copyright file="Config.cs" company="honeplusのメモ帳">
6 //      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
7 // <author>
8 //      Honeplus</author>
9 // ================================================================================================
10
11 namespace Honememo.Wptscs.Models
12 {
13     using System;
14     using System.Collections.Generic;
15     using System.IO;
16     using System.Windows.Forms;
17     using System.Xml;
18     using System.Xml.Serialization;
19     using Honememo.Utilities;
20     using Honememo.Wptscs.Logics;
21     using Honememo.Wptscs.Properties;
22     using Honememo.Wptscs.Utilities;
23     using Honememo.Wptscs.Websites;
24
25     /// <summary>
26     /// アプリケーションの設定を保持するクラスです。
27     /// </summary>
28     public class Config : IXmlSerializable
29     {
30         #region private変数
31
32         /// <summary>
33         /// ウェブサイトの情報。
34         /// </summary>
35         private IList<Website> websites = new List<Website>();
36
37         /// <summary>
38         /// 言語間の項目の対訳表。
39         /// </summary>
40         private IList<TranslationDictionary> itemTables = new List<TranslationDictionary>();
41
42         /// <summary>
43         /// 言語間の見出しの対訳表。
44         /// </summary>
45         private TranslationTable headingTable = new TranslationTable();
46
47         #endregion
48
49         #region コンストラクタ
50
51         /// <summary>
52         /// 空のインスタンスを生成する。
53         /// </summary>
54         /// <remarks>通常は<see cref="GetInstance(string)"/>で設定ファイルから読み込む。</remarks>
55         public Config()
56         {
57         }
58
59         #endregion
60         
61         #region プロパティ
62
63         /// <summary>
64         /// 設定ファイルと紐付いている場合のファイル名。
65         /// </summary>
66         /// <remarks>ファイルと紐付いていない場合は空。</remarks>
67         public string File
68         {
69             get;
70             set;
71         }
72
73         /// <summary>
74         /// 翻訳支援処理で使用するロジッククラス名。
75         /// </summary>
76         public Type Translator
77         {
78             get;
79             set;
80         }
81
82         /// <summary>
83         /// ウェブサイトの情報。
84         /// </summary>
85         /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
86         /// <remarks>空でもオブジェクトは存在。</remarks>
87         public IList<Website> Websites
88         {
89             get
90             {
91                 return this.websites;
92             }
93
94             set
95             {
96                 this.websites = Validate.NotNull(value);
97             }
98         }
99
100         /// <summary>
101         /// 言語間の項目の対訳表。
102         /// </summary>
103         /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
104         /// <remarks>空でもオブジェクトは存在。</remarks>
105         public IList<TranslationDictionary> ItemTables
106         {
107             get
108             {
109                 return this.itemTables;
110             }
111
112             set
113             {
114                 this.itemTables = Validate.NotNull(value);
115             }
116         }
117
118         /// <summary>
119         /// 言語間の見出しの対訳表。
120         /// </summary>
121         /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
122         /// <remarks>空でもオブジェクトは存在。</remarks>
123         public TranslationTable HeadingTable
124         {
125             get
126             {
127                 return this.headingTable;
128             }
129
130             set
131             {
132                 this.headingTable = Validate.NotNull(value);
133             }
134         }
135
136         #endregion
137
138         #region 静的メソッド
139
140         /// <summary>
141         /// アプリケーションの設定を取得する。
142         /// ユーザーごとの設定ファイルがあればその内容を、
143         /// なければアプリケーション標準の設定ファイルの内容を
144         /// 読み込んで、インスタンスを作成する。
145         /// </summary>
146         /// <param name="file">設定ファイル名。</param>
147         /// <returns>作成したインスタンス。</returns>
148         /// <exception cref="FileNotFoundException">指定されたファイルがどこにも存在しない場合。</exception>
149         public static Config GetInstance(string file)
150         {
151             // ユーザーごと・または初期設定用の設定ファイルを読み込み
152             string path = FormUtils.SearchUserAppData(file, Settings.Default.ConfigurationCompatible);
153             if (String.IsNullOrEmpty(path))
154             {
155                 // どこにも無い場合は例外を投げる
156                 throw new FileNotFoundException(file + " is not found");
157             }
158
159             // 設定ファイルを読み込み、読み込み元のファイル名は記録しておく
160             System.Diagnostics.Debug.WriteLine("Config.GetInstance > " + path + " を読み込み");
161             using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
162             {
163                 Config config = new XmlSerializer(typeof(Config)).Deserialize(stream) as Config;
164                 config.File = file;
165                 return config;
166             }
167         }
168
169         #endregion
170
171         #region インスタンスメソッド
172
173         /// <summary>
174         /// 設定をユーザーごとの設定ファイルに書き出し。
175         /// </summary>
176         /// <exception cref="InvalidOperationException">
177         /// <see cref="File"/>にこのインスタンスと紐付くファイル名が指定されていない場合。
178         /// </exception>
179         public void Save()
180         {
181             // このインスタンスとファイルが紐付いていない場合、実行不可
182             if (String.IsNullOrWhiteSpace(this.File))
183             {
184                 throw new InvalidOperationException("file is empty");
185             }
186
187             // 最初にディレクトリの有無を確認し作成
188             string path = Application.UserAppDataPath;
189             if (!Directory.Exists(path))
190             {
191                 Directory.CreateDirectory(path);
192             }
193
194             // 設定ファイルを出力
195             using (Stream stream = new FileStream(Path.Combine(path, this.File), FileMode.Create))
196             {
197                 new XmlSerializer(typeof(Config)).Serialize(stream, this);
198             }
199         }
200         
201         #endregion
202
203         #region 設定値取得用インスタンスメソッド
204
205         /// <summary>
206         /// 設定から、現在の処理対象・指定された言語のウェブサイトを取得する。
207         /// </summary>
208         /// <param name="lang">言語コード。</param>
209         /// <returns>ウェブサイトの情報。存在しない場合は<c>null</c>返す。</returns>
210         public Website GetWebsite(string lang)
211         {
212             // 設定が存在すれば取得した値を返す
213             foreach (Website s in this.Websites)
214             {
215                 if (s.Language.Code == lang)
216                 {
217                     return s;
218                 }
219             }
220
221             // 存在しない場合、nullを返す
222             // ※ こっちがNeedCreateじゃないのは、何をnewすればいいのか判らないため
223             return null;
224         }
225         
226         /// <summary>
227         /// 設定から、現在の処理対象・指定された言語の対訳表(項目)を取得する。
228         /// </summary>
229         /// <param name="from">翻訳元言語。</param>
230         /// <param name="to">翻訳先言語。</param>
231         /// <returns>対訳表の情報。存在しない場合は新たに作成した対訳表を返す。</returns>
232         public TranslationDictionary GetItemTableNeedCreate(string from, string to)
233         {
234             // オブジェクトに用意されている共通メソッドをコール
235             return TranslationDictionary.GetDictionaryNeedCreate(this.ItemTables, from, to);
236         }
237
238         #endregion
239
240         #region XMLシリアライズ用メソッド
241
242         /// <summary>
243         /// シリアライズするXMLのスキーマ定義を返す。
244         /// </summary>
245         /// <returns>XML表現を記述する<see cref="System.Xml.Schema.XmlSchema"/>。</returns>
246         public System.Xml.Schema.XmlSchema GetSchema()
247         {
248             return null;
249         }
250
251         /// <summary>
252         /// XMLからオブジェクトを読み込む。
253         /// </summary>
254         /// <param name="reader">読込元の<see cref="XmlReader"/>。</param>
255         public void ReadXml(XmlReader reader)
256         {
257             XmlDocument xml = new XmlDocument();
258             xml.Load(reader);
259
260             // ルートエレメントを取得
261             // ※ 以下、基本的に無かったらNGの部分はいちいちチェックしない。例外飛ばす
262             XmlElement rootElement = xml.DocumentElement;
263
264             // ロジッククラス
265             this.Translator = this.ParseTranslator(rootElement.SelectSingleNode("Translator").InnerText);
266
267             // Webサイト
268             foreach (XmlNode siteNode in rootElement.SelectSingleNode("Websites").ChildNodes)
269             {
270                 // ノードに指定された内容に応じたインスタンスを取得する
271                 this.Websites.Add(this.ParseWebsite(siteNode, reader.Settings));
272             }
273
274             // 項目の対訳表
275             XmlSerializer serializer = new XmlSerializer(typeof(TranslationDictionary), new XmlRootAttribute("ItemTable"));
276             foreach (XmlNode itemNode in rootElement.SelectSingleNode("ItemTables").ChildNodes)
277             {
278                 using (XmlReader r = XmlReader.Create(new StringReader(itemNode.OuterXml), reader.Settings))
279                 {
280                     this.ItemTables.Add(serializer.Deserialize(r) as TranslationDictionary);
281                 }
282             }
283
284             // 見出しの対訳表
285             using (XmlReader r = XmlReader.Create(
286                 new StringReader(rootElement.SelectSingleNode("HeadingTable").OuterXml),
287                 reader.Settings))
288             {
289                 this.HeadingTable = new XmlSerializer(typeof(TranslationTable), new XmlRootAttribute("HeadingTable"))
290                     .Deserialize(r) as TranslationTable;
291             }
292         }
293
294         /// <summary>
295         /// オブジェクトをXMLに出力する。
296         /// </summary>
297         /// <param name="writer">出力先の<see cref="XmlWriter"/>。</param>
298         public void WriteXml(XmlWriter writer)
299         {
300             // ロジッククラス
301             string translator = this.Translator.FullName;
302             if (translator.StartsWith(typeof(Translator).Namespace))
303             {
304                 // 自前のエンジンの場合、クラス名だけを出力
305                 translator = this.Translator.Name;
306             }
307
308             writer.WriteElementString("Translator", translator);
309
310             // 各処理モードのWebサイト
311             writer.WriteStartElement("Websites");
312             foreach (Website site in this.Websites)
313             {
314                 // 通常はサイトのパッケージ名も含めたフル名を要素名とする
315                 string siteName = site.GetType().FullName;
316                 if (siteName.StartsWith(typeof(Website).Namespace))
317                 {
318                     // 自前のサイトの場合、クラス名だけを出力
319                     siteName = site.GetType().Name;
320                 }
321
322                 new XmlSerializer(site.GetType(), new XmlRootAttribute(siteName)).Serialize(writer, site);
323             }
324
325             writer.WriteEndElement();
326
327             // 項目の対訳表
328             XmlSerializer serializer = new XmlSerializer(typeof(TranslationDictionary), new XmlRootAttribute("ItemTable"));
329             writer.WriteStartElement("ItemTables");
330             foreach (TranslationDictionary trans in this.ItemTables)
331             {
332                 serializer.Serialize(writer, trans);
333             }
334
335             writer.WriteEndElement();
336
337             // 見出しの対訳表
338             new XmlSerializer(this.HeadingTable.GetType(), new XmlRootAttribute("HeadingTable"))
339                 .Serialize(writer, this.HeadingTable);
340         }
341
342         /// <summary>
343         /// 指定されたXML値から<see cref="Translator"/>のクラスを取得する。
344         /// </summary>
345         /// <param name="name">XMLのクラス名情報。</param>
346         /// <returns><see cref="Translator"/>クラス。</returns>
347         /// <remarks>クラスは動的に判定する。クラスが存在しない場合などは随時状況に応じた例外を投げる。</remarks>
348         private Type ParseTranslator(string name)
349         {
350             // Translateと同じパッケージに指定された名前のクラスがあるかを探す
351             Type type = Type.GetType(typeof(Translator).Namespace + "." + name, false, true);
352             if (type == null)
353             {
354                 // 存在しない場合、そのままの名前でクラスを探索、無ければ例外スロー
355                 type = Type.GetType(name, true, true);
356             }
357
358             return type;
359         }
360
361         /// <summary>
362         /// XMLノードから<see cref="Website"/>インスタンスをデシリアライズする。
363         /// </summary>
364         /// <param name="node"><see cref="Website"/>をシリアライズしたノード。</param>
365         /// <param name="setting">XML読み込み時の設定。</param>
366         /// <returns>デシリアライズしたインスタンス。</returns>
367         /// <remarks>クラスはノード名から動的に判定する。クラスが存在しない場合などは随時状況に応じた例外を投げる。</remarks>
368         private Website ParseWebsite(XmlNode node, XmlReaderSettings setting)
369         {
370             // WebSiteと同じパッケージに指定された名前のクラスがあるかを探す
371             Type type = Type.GetType(typeof(Website).Namespace + "." + node.Name, false, true);
372             if (type == null)
373             {
374                 // 存在しない場合、そのままの名前でクラスを探索、無ければ例外スロー
375                 type = Type.GetType(node.Name, true, true);
376             }
377
378             using (XmlReader r = XmlReader.Create(new StringReader(node.OuterXml), setting))
379             {
380                 return new XmlSerializer(type).Deserialize(r) as Website;
381             }
382         }
383
384         #endregion
385     }
386 }