OSDN Git Service

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