1 // ================================================================================================
3 // 翻訳支援処理を実装するための抽象クラスソース</summary>
5 // <copyright file="Translator.cs" company="honeplusのメモ帳">
6 // Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
9 // ================================================================================================
11 namespace Honememo.Wptscs.Logics
14 using System.Diagnostics;
17 using System.Net.NetworkInformation;
18 using System.Reflection;
19 using Honememo.Models;
20 using Honememo.Utilities;
21 using Honememo.Wptscs.Models;
22 using Honememo.Wptscs.Properties;
23 using Honememo.Wptscs.Utilities;
24 using Honememo.Wptscs.Websites;
27 /// 翻訳支援処理を実装するための抽象クラスです。
29 public abstract class Translator
36 private string text = String.Empty;
41 private Logger logger;
52 // ステータス管理については更新イベントを連鎖させる
53 this.Stopwatch = new Stopwatch();
54 this.Logger = new Logger();
55 this.StatusManager = new StatusManager<string>();
56 this.StatusManager.Changed += new EventHandler(
59 if (this.StatusUpdated != null)
61 this.StatusUpdated(this, EventArgs.Empty);
73 public event EventHandler LogUpdated;
78 public event EventHandler StatusUpdated;
105 public TranslationDictionary ItemTable
114 public TranslationTable HeadingTable
127 return this.Logger.ToString();
138 // 内部的に実際に管理しているのはStatusManager
139 return StringUtils.DefaultString(this.StatusManager.Status);
146 public Stopwatch Stopwatch
164 this.text = StringUtils.DefaultString(value);
169 /// 処理を途中で終了させるためのフラグ。
171 public bool CancellationPending
184 /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
185 protected Logger Logger
194 // nullは不可。また、ロガー変更後はイベントを設定
195 this.logger = Validate.NotNull(value);
196 this.logger.LogUpdate += new EventHandler(
199 if (this.LogUpdated != null)
201 this.LogUpdated(this, EventArgs.Empty);
210 /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
211 protected StatusManager<string> StatusManager
222 /// 翻訳支援処理のインスタンスを作成。
224 /// <param name="config">アプリケーション設定。</param>
225 /// <param name="from">翻訳元言語。</param>
226 /// <param name="to">翻訳先言語。</param>
227 /// <returns>生成したインスタンス。</returns>
229 /// 設定は設定クラスより取得、無ければ一部自動生成する。
230 /// インスタンス生成失敗時は例外を投げる。
232 public static Translator Create(Config config, string from, string to)
234 // 処理対象に応じてTranslatorを継承したオブジェクトを生成
235 ConstructorInfo constructor = config.Translator.GetConstructor(Type.EmptyTypes);
236 if (constructor == null)
238 throw new NotImplementedException(config.Translator.FullName + " default constructor is not found");
241 // 設定に指定されたクラスを、引数無しのコンストラクタを用いて生成する
242 Translator translator = (Translator)constructor.Invoke(null);
245 translator.From = config.GetWebsite(from);
246 translator.To = config.GetWebsite(to);
249 translator.ItemTable = config.GetItemTableNeedCreate(from, to);
251 // 対訳表(見出し)の設定、使用する言語は決まっているので組み合わせを設定
252 translator.HeadingTable = config.HeadingTable;
253 translator.HeadingTable.From = from;
254 translator.HeadingTable.To = to;
266 /// <param name="name">記事名。</param>
267 /// <exception cref="ApplicationException">処理が中断された場合。中断の理由は<see cref="Logger"/>に出力される。</exception>
268 /// <exception cref="InvalidOperationException"><see cref="From"/>, <see cref="To"/>が設定されていない場合。</exception>
269 public void Run(string name)
271 // ※必須な情報が設定されていない場合、InvalidOperationExceptionを返す
272 if (this.From == null || this.To == null)
274 throw new InvalidOperationException("From or To is null");
279 this.Stopwatch.Start();
282 string host = new Uri(this.From.Location).Host;
283 if (!String.IsNullOrEmpty(host) && !Settings.Default.IgnoreError)
285 if (!this.Ping(host))
287 throw new ApplicationException("ping failed");
291 // ここまでの間に終了要求が出ているかを確認
292 this.ThrowExceptionIfCanceled();
302 // 終了後は処理状態をクリア、処理時間を測定終了
303 this.StatusManager.Clear();
304 this.Stopwatch.Stop();
310 #region 実装が必要なテンプレートメソッド
315 /// <param name="name">記事名。</param>
316 /// <exception cref="ApplicationException">処理を中断する場合。中断の理由は<see cref="Logger"/>に出力する。</exception>
317 /// <remarks>テンプレートメソッド的な構造になっています。</remarks>
318 protected abstract void RunBody(string name);
325 /// ログ出力によるエラー処理を含んだページ取得処理。
327 /// <param name="title">ページタイトル。</param>
328 /// <param name="page">取得したページ。ページが存在しない場合は <c>null</c> を返す。</param>
329 /// <returns>処理が成功した(404も含む)場合<c>true</c>、失敗した(通信エラーなど)の場合<c>false</c>。</returns>
330 /// <exception cref="ApplicationException"><see cref="CancellationPending"/>が<c>true</c>の場合。</exception>
332 /// 本メソッドは、大きく3パターンの動作を行う。
333 /// <list type="number">
334 /// <item><description>正常にページが取得できた → <c>true</c>でページを設定、ログ出力無し</description></item>
335 /// <item><description>404など想定内の例外でページが取得できなかった → <c>true</c>でページ無し、ログ出力無し</description></item>
336 /// <item><description>想定外の例外でページが取得できなかった → <c>false</c>でページ無し、ログ出力有り
337 /// or <c>ApplicationException</c>で処理中断(アプリケーション設定のIgnoreErrorによる)。</description></item>
339 /// また、実行中は処理状態をサーバー接続中に更新する。
340 /// 実行前後には終了要求のチェックも行う。
342 protected bool TryGetPage(string title, out Page page)
344 // 通信開始の前に終了要求が出ているかを確認
345 this.ThrowExceptionIfCanceled();
347 // ページ取得処理、実行中は処理状態を変更
349 using (var sm = this.StatusManager.Switch(Resources.StatusDownloading))
351 success = this.TryGetPageBody(title, out page);
355 this.ThrowExceptionIfCanceled();
360 /// 終了要求が出ている場合、例外を投げる。
362 /// <exception cref="ApplicationException"><see cref="CancellationPending"/>が<c>true</c>の場合。</exception>
363 protected void ThrowExceptionIfCanceled()
365 if (this.CancellationPending)
367 throw new ApplicationException("CancellationPending is true");
378 private void Initialize()
382 this.StatusManager.Clear();
383 this.Stopwatch.Reset();
384 this.Text = String.Empty;
385 this.CancellationPending = false;
391 /// <param name="server">サーバー名。</param>
392 /// <returns><c>true</c> 接続成功。</returns>
393 /// <remarks>実行中は処理状態をサーバー接続中に更新する。</remarks>
394 private bool Ping(string server)
396 // サーバー接続チェック、実行中は処理状態を変更
397 using (var sm = this.StatusManager.Switch(Resources.StatusPinging))
400 Ping ping = new Ping();
403 PingReply reply = ping.Send(server);
404 if (reply.Status != IPStatus.Success)
406 this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, reply.Status.ToString());
412 this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, e.InnerException.Message);
421 /// ログ出力によるエラー処理を含んだページ取得処理本体。
423 /// <param name="title">ページタイトル。</param>
424 /// <param name="page">取得したページ。ページが存在しない場合は <c>null</c> を返す。</param>
425 /// <returns>処理が成功した(404も含む)場合<c>true</c>、失敗した(通信エラーなど)の場合<c>false</c>。</returns>
427 /// 本メソッドは、大きく3パターンの動作を行う。
428 /// <list type="number">
429 /// <item><description>正常にページが取得できた → <c>true</c>でページを設定、ログ出力無し</description></item>
430 /// <item><description>404など想定内の例外でページが取得できなかった → <c>true</c>でページ無し、ログ出力無し</description></item>
431 /// <item><description>想定外の例外でページが取得できなかった → <c>false</c>でページ無し、ログ出力有り
432 /// or <c>ApplicationException</c>で処理中断(アプリケーション設定のIgnoreError等による)。</description></item>
435 private bool TryGetPageBody(string title, out Page page)
441 page = this.From.GetPage(title);
444 catch (FileNotFoundException)
449 catch (EndPeriodException)
451 // 末尾がピリオドで終わるページが処理できない既知の不具合への対応、警告メッセージを出す
452 this.Logger.AddResponse(Resources.LogMessageErrorPageName, title);
457 // その他例外の場合、まずエラー情報を出力
458 this.Logger.AddError(e);
460 // エラーを無視しない場合、ここで翻訳支援処理を中断する
461 if (!Settings.Default.IgnoreError)
463 throw new ApplicationException(e.Message, e);