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.Utilities;
20 using Honememo.Wptscs.Models;
21 using Honememo.Wptscs.Properties;
22 using Honememo.Wptscs.Websites;
25 /// 翻訳支援処理を実装するための共通クラスです。
27 public abstract class Translator
34 private string status = String.Empty;
39 private string text = String.Empty;
44 private Logger logger;
55 this.Stopwatch = new Stopwatch();
56 this.Logger = new Logger();
64 /// <see cref="ChangeStatusInExecuting"/> で実行する処理のためのデリゲート。
66 protected delegate void MethodWithChangeStatus();
75 public event EventHandler LogUpdate;
80 public event EventHandler StatusUpdate;
89 public TranslationDictionary ItemTable
98 public TranslationTable HeadingTable
111 return this.Logger.ToString();
127 this.status = StringUtils.DefaultString(value);
128 if (this.StatusUpdate != null)
130 this.StatusUpdate(this, EventArgs.Empty);
138 public Stopwatch Stopwatch
156 this.text = StringUtils.DefaultString(value);
161 /// 処理を途中で終了させるためのフラグ。
163 public bool CancellationPending
190 protected Logger Logger
199 // nullは不可。また、ロガー変更後はイベントを設定
200 this.logger = Validate.NotNull(value);
201 this.logger.LogUpdate += this.GetLogUpdate;
210 /// 翻訳支援処理のインスタンスを作成。
212 /// <param name="config">アプリケーション設定。</param>
213 /// <param name="from">翻訳元言語。</param>
214 /// <param name="to">翻訳先言語。</param>
215 /// <returns>生成したインスタンス。</returns>
217 /// 設定は設定クラスより取得、無ければ一部自動生成する。
218 /// インスタンス生成失敗時は例外を投げる。
220 public static Translator Create(Config config, string from, string to)
222 // 処理対象に応じてTranslatorを継承したオブジェクトを生成
223 ConstructorInfo constructor = config.Translator.GetConstructor(Type.EmptyTypes);
224 if (constructor == null)
226 throw new NotImplementedException(config.Translator.FullName + " default constructor is not found");
229 // 設定に指定されたクラスを、引数無しのコンストラクタを用いて生成する
230 Translator translator = (Translator)constructor.Invoke(null);
233 translator.From = config.GetWebsite(from);
234 translator.To = config.GetWebsite(to);
237 translator.ItemTable = config.GetItemTableNeedCreate(from, to);
239 // 対訳表(見出し)の設定、使用する言語は決まっているので組み合わせを設定
240 translator.HeadingTable = config.HeadingTable;
241 translator.HeadingTable.From = from;
242 translator.HeadingTable.To = to;
254 /// <param name="name">記事名。</param>
255 /// <exception cref="ApplicationException">処理が中断された場合。中断の理由は<see cref="Logger"/>に出力される。</exception>
256 /// <exception cref="InvalidOperationException"><see cref="From"/>, <see cref="To"/>が設定されていない場合。</exception>
257 public void Run(string name)
259 // ※必須な情報が設定されていない場合、InvalidOperationExceptionを返す
260 if (this.From == null || this.To == null)
262 throw new InvalidOperationException("From or To is null");
267 this.Stopwatch.Start();
270 string host = new Uri(this.From.Location).Host;
271 if (!String.IsNullOrEmpty(host) && !Settings.Default.IgnoreError)
273 if (!this.Ping(host))
275 throw new ApplicationException("ping failed");
279 // ここまでの間に終了要求が出ているかを確認
280 this.ThrowExceptionIfCanceled();
290 // 終了後は処理状態をクリア、処理時間を測定終了
291 this.Status = String.Empty;
292 this.Stopwatch.Stop();
298 #region protectedメソッド
303 /// <param name="name">記事名。</param>
304 /// <exception cref="ApplicationException">処理を中断する場合。中断の理由は<see cref="Logger"/>に出力する。</exception>
305 /// <remarks>テンプレートメソッド的な構造になっています。</remarks>
306 protected abstract void RunBody(string name);
309 /// ログ出力によるエラー処理を含んだページ取得処理。
311 /// <param name="title">ページタイトル。</param>
312 /// <param name="page">取得したページ。ページが存在しない場合は <c>null</c> を返す。</param>
313 /// <returns>処理が成功した(404も含む)場合<c>true</c>、失敗した(通信エラーなど)の場合<c>false</c>。</returns>
314 /// <exception cref="ApplicationException"><see cref="CancellationPending"/>が<c>true</c>の場合。</exception>
316 /// 本メソッドは、大きく3パターンの動作を行う。
317 /// <list type="number">
318 /// <item><description>正常にページが取得できた → <c>true</c>でページを設定、ログ出力無し</description></item>
319 /// <item><description>404など想定内の例外でページが取得できなかった → <c>true</c>でページ無し、ログ出力無し</description></item>
320 /// <item><description>想定外の例外でページが取得できなかった → <c>false</c>でページ無し、ログ出力有り
321 /// or <c>ApplicationException</c>で処理中断(アプリケーション設定のIgnoreErrorによる)。</description></item>
323 /// また、実行中は処理状態をサーバー接続中に更新する。
324 /// 実行前後には終了要求のチェックも行う。
326 protected bool TryGetPage(string title, out Page page)
328 // 通信開始の前に終了要求が出ているかを確認
329 this.ThrowExceptionIfCanceled();
331 // ページ取得処理、実行中は処理状態を変更
332 bool success = false;
334 this.ChangeStatusInExecuting(
335 () => success = this.TryGetPageBody(title, out result),
336 Resources.StatusDownloading);
340 this.ThrowExceptionIfCanceled();
345 /// 終了要求が出ている場合、例外を投げる。
347 /// <exception cref="ApplicationException"><see cref="CancellationPending"/>が<c>true</c>の場合。</exception>
348 protected void ThrowExceptionIfCanceled()
350 if (this.CancellationPending)
352 throw new ApplicationException("CancellationPending is true");
357 /// 指定された処理を実行する間、処理状態を渡された値に更新する。
358 /// 処理終了後は以前の処理状態に戻す。
360 /// <param name="method">実行する処理。</param>
361 /// <param name="status">処理状態。</param>
362 protected void ChangeStatusInExecuting(MethodWithChangeStatus method, string status)
364 // 現在の処理状態を保存、新しい処理状態をセットし、処理を実行する
365 string oldStatus = this.Status;
366 this.Status = status;
374 this.Status = oldStatus;
385 private void Initialize()
389 this.Status = String.Empty;
390 this.Stopwatch.Reset();
391 this.Text = String.Empty;
392 this.CancellationPending = false;
398 /// <param name="server">サーバー名。</param>
399 /// <returns><c>true</c> 接続成功。</returns>
400 /// <remarks>実行中は処理状態をサーバー接続中に更新する。</remarks>
401 private bool Ping(string server)
403 // サーバー接続チェック、実行中は処理状態を変更
405 this.ChangeStatusInExecuting(
406 () => result = this.PingBody(server),
407 Resources.StatusPinging);
414 /// <param name="server">サーバー名。</param>
415 /// <returns><c>true</c> 接続成功。</returns>
416 private bool PingBody(string server)
419 Ping ping = new Ping();
422 PingReply reply = ping.Send(server);
423 if (reply.Status != IPStatus.Success)
425 this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, reply.Status.ToString());
431 this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, e.InnerException.Message);
439 /// ログ出力によるエラー処理を含んだページ取得処理本体。
441 /// <param name="title">ページタイトル。</param>
442 /// <param name="page">取得したページ。ページが存在しない場合は <c>null</c> を返す。</param>
443 /// <returns>処理が成功した(404も含む)場合<c>true</c>、失敗した(通信エラーなど)の場合<c>false</c>。</returns>
445 /// 本メソッドは、大きく3パターンの動作を行う。
446 /// <list type="number">
447 /// <item><description>正常にページが取得できた → <c>true</c>でページを設定、ログ出力無し</description></item>
448 /// <item><description>404など想定内の例外でページが取得できなかった → <c>true</c>でページ無し、ログ出力無し</description></item>
449 /// <item><description>想定外の例外でページが取得できなかった → <c>false</c>でページ無し、ログ出力有り
450 /// or <c>ApplicationException</c>で処理中断(アプリケーション設定のIgnoreErrorによる)。</description></item>
453 private bool TryGetPageBody(string title, out Page page)
459 page = this.From.GetPage(title);
462 catch (FileNotFoundException)
469 // その他例外の場合、まずエラー情報を出力
470 this.Logger.AddResponse(e.Message);
471 if (e is WebException && ((WebException)e).Response != null)
473 // 出せるならエラーとなったURLも出力
474 this.Logger.AddResponse(Resources.LogMessageErrorURL, ((WebException)e).Response.ResponseUri);
477 // エラーを無視しない場合、ここで翻訳支援処理を中断する
478 if (!Settings.Default.IgnoreError)
480 throw new ApplicationException(e.Message, e);
490 /// <param name="sender">イベント発生オブジェクト。</param>
491 /// <param name="e">発生したイベント。</param>
492 private void GetLogUpdate(object sender, EventArgs e)
494 // もともとこのクラスにあったログ通知イベントをロガーに移動したため、入れ子で呼び出す
495 if (this.LogUpdate != null)
497 this.LogUpdate(this, EventArgs.Empty);