OSDN Git Service

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