OSDN Git Service

#27617 各サイトでの動作を確認し設定を精査・ツールチップの文言を姉妹サイトも考慮したものに修正,
[wptscs/wpts.git] / Wptscs / Logics / Translator.cs
index a133941..a36947f 100644 (file)
@@ -1,6 +1,6 @@
 // ================================================================================================
 // <summary>
-//      翻訳支援処理を実装するための共通クラスソース</summary>
+//      翻訳支援処理を実装するための抽象クラスソース</summary>
 //
 // <copyright file="Translator.cs" company="honeplusのメモ帳">
 //      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
@@ -16,24 +16,21 @@ namespace Honememo.Wptscs.Logics
     using System.Net;
     using System.Net.NetworkInformation;
     using System.Reflection;
+    using Honememo.Models;
     using Honememo.Utilities;
     using Honememo.Wptscs.Models;
     using Honememo.Wptscs.Properties;
+    using Honememo.Wptscs.Utilities;
     using Honememo.Wptscs.Websites;
 
     /// <summary>
-    /// 翻訳支援処理を実装するための共通クラスです。
+    /// 翻訳支援処理を実装するための抽象クラスです。
     /// </summary>
     public abstract class Translator
     {
         #region private変数
 
         /// <summary>
-        /// 処理状態メッセージ。
-        /// </summary>
-        private string status = String.Empty;
-
-        /// <summary>
         /// 変換後テキスト。
         /// </summary>
         private string text = String.Empty;
@@ -48,40 +45,59 @@ namespace Honememo.Wptscs.Logics
         #region コンストラクタ
 
         /// <summary>
-        /// インスタンスを生成する。
+        /// ã\83\88ã\83©ã\83³ã\82¹ã\83¬ã\83¼ã\82¿ã\82¤ã\83³ã\82¹ã\82¿ã\83³ã\82¹ã\82\92ç\94\9fæ\88\90ã\81\99ã\82\8bã\80\82
         /// </summary>
         public Translator()
         {
+            // ステータス管理については更新イベントを連鎖させる
             this.Stopwatch = new Stopwatch();
             this.Logger = new Logger();
+            this.StatusManager = new StatusManager<string>();
+            this.StatusManager.Changed += new EventHandler(
+                delegate
+                {
+                    if (this.StatusUpdated != null)
+                    {
+                        this.StatusUpdated(this, EventArgs.Empty);
+                    }
+                });
         }
 
         #endregion
 
-        #region デリゲート
-
-        /// <summary>
-        /// <see cref="ChangeStatusInExecuting"/> で実行する処理のためのデリゲート。
-        /// </summary>
-        protected delegate void MethodWithChangeStatus();
-
-        #endregion
-
         #region イベント
 
         /// <summary>
         /// ログ更新伝達イベント。
         /// </summary>
-        public event EventHandler LogUpdate;
+        public event EventHandler LogUpdated;
 
         /// <summary>
         /// 処理状態更新伝達イベント。
         /// </summary>
-        public event EventHandler StatusUpdate;
+        public event EventHandler StatusUpdated;
 
         #endregion
 
-        #region プロパティ
+        #region 公開プロパティ
+
+        /// <summary>
+        /// 翻訳元言語のサイト。
+        /// </summary>
+        public Website From
+        {
+            get;
+            set;
+        }
+
+        /// <summary>
+        /// 翻訳先言語のサイト。
+        /// </summary>
+        public Website To
+        {
+            get;
+            set;
+        }
 
         /// <summary>
         /// 言語間の項目の対訳表。
@@ -119,16 +135,8 @@ namespace Honememo.Wptscs.Logics
         {
             get
             {
-                return this.status;
-            }
-
-            protected set
-            {
-                this.status = StringUtils.DefaultString(value);
-                if (this.StatusUpdate != null)
-                {
-                    this.StatusUpdate(this, EventArgs.Empty);
-                }
+                // 内部的に実際に管理しているのはStatusManager
+                return StringUtils.DefaultString(this.StatusManager.Status);
             }
         }
 
@@ -166,27 +174,14 @@ namespace Honememo.Wptscs.Logics
             set;
         }
 
-        /// <summary>
-        /// 翻訳元言語のサイト。
-        /// </summary>
-        public Website From
-        {
-            get;
-            set;
-        }
+        #endregion
 
-        /// <summary>
-        /// 翻訳先言語のサイト。
-        /// </summary>
-        public Website To
-        {
-            get;
-            set;
-        }
+        #region 実装支援用プロパティ
 
         /// <summary>
         /// ログテキスト生成用ロガー。
         /// </summary>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
         protected Logger Logger
         {
             get
@@ -198,24 +193,43 @@ namespace Honememo.Wptscs.Logics
             {
                 // nullは不可。また、ロガー変更後はイベントを設定
                 this.logger = Validate.NotNull(value);
-                this.logger.LogUpdate += this.GetLogUpdate;
+                this.logger.LogUpdate += new EventHandler(
+                    delegate
+                    {
+                        if (this.LogUpdated != null)
+                        {
+                            this.LogUpdated(this, EventArgs.Empty);
+                        }
+                    });
             }
         }
 
+        /// <summary>
+        /// ステータス管理用オブジェクト。
+        /// </summary>
+        protected StatusManager<string> StatusManager
+        {
+            get;
+            private set;
+        }
+
         #endregion
 
         #region 静的メソッド
 
         /// <summary>
-        /// 翻訳支援処理のインスタンスを作成
+        /// 指定されたアプリケーション設定から翻訳支援処理のインスタンスを作成する
         /// </summary>
         /// <param name="config">アプリケーション設定。</param>
         /// <param name="from">翻訳元言語。</param>
         /// <param name="to">翻訳先言語。</param>
         /// <returns>生成したインスタンス。</returns>
+        /// <exception cref="NotImplementedException">
+        /// アプリケーション設定に指定されているトランスレータに引数無しのコンストラクタが存在しない場合。
+        /// </exception>
         /// <remarks>
         /// 設定は設定クラスより取得、無ければ一部自動生成する。
-        /// インスタンス生成失敗時は例外を投げる。
+        /// インスタンス生成失敗時は各種例外を投げる。
         /// </remarks>
         public static Translator Create(Config config, string from, string to)
         {
@@ -246,7 +260,7 @@ namespace Honememo.Wptscs.Logics
 
         #endregion
 
-        #region publicメソッド
+        #region 公開メソッド
 
         /// <summary>
         /// 翻訳支援処理実行。
@@ -262,40 +276,37 @@ namespace Honememo.Wptscs.Logics
                 throw new InvalidOperationException("From or To is null");
             }
 
-            // å¤\89æ\95°ã\82\92å\88\9dæ\9c\9få\8c\96ã\80\81å\87¦ç\90\86æ\99\82é\96\93ã\82\92測å®\9aé\96\8bå§\8b
+            // å\88\9dæ\9c\9få\8c\96ã\82\84ã\82¹ã\83\88ã\83\83ã\83\97ã\82¦ã\82©ã\83\83ã\83\81ã\81®èµ·å\8b\95ã\81¨ã\81\84ã\81£ã\81\9få\89\8då\87¦ç\90\86ã\82\92å®\9fæ\96½
             this.Initialize();
-            this.Stopwatch.Start();
-
-            // サーバー接続チェック
-            string host = new Uri(this.From.Location).Host;
-            if (!String.IsNullOrEmpty(host) && !Settings.Default.IgnoreError)
+            try
             {
-                if (!this.Ping(host))
+                // サーバー接続チェック
+                string host = new Uri(this.From.Location).Host;
+                if (!String.IsNullOrEmpty(host) && !Settings.Default.IgnoreError)
                 {
-                    throw new ApplicationException("ping failed");
+                    if (!this.Ping(host))
+                    {
+                        throw new ApplicationException("ping failed");
+                    }
                 }
-            }
 
-            // ここまでの間に終了要求が出ているかを確認
-            this.ThrowExceptionIfCanceled();
+                // ここまでの間に終了要求が出ているかを確認
+                this.ThrowExceptionIfCanceled();
 
-            // 翻訳支援処理実行部の本体を実行
-            // ※以降の処理は、継承クラスにて定義
-            try
-            {
+                // 翻訳支援処理実行部の本体を実行
+                // ※以降の処理は、継承クラスにて定義
                 this.RunBody(name);
             }
             finally
             {
-                // 終了後は処理状態をクリア、処理時間を測定終了
-                this.Status = String.Empty;
-                this.Stopwatch.Stop();
+                // 状態のクリアやストップウォッチの停止といった後処理を実施
+                this.Terminate();
             }
         }
         
         #endregion
 
-        #region protectedメソッド
+        #region テンプレートメソッド
 
         /// <summary>
         /// 翻訳支援処理実行部の本体。
@@ -306,19 +317,73 @@ namespace Honememo.Wptscs.Logics
         protected abstract void RunBody(string name);
 
         /// <summary>
+        /// 翻訳支援処理実行時の初期化処理。
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// <see cref="RunBody"/>実行前の初期化のタイミングでコールされる。
+        /// 本メソッドで例外が発生した場合、<see cref="RunBody"/>, <see cref="Terminate"/>はコールされない。
+        /// </para>
+        /// <para>
+        /// オーバーライド時は必ず親のメソッドもコールすること。
+        /// </para>
+        /// </remarks>
+        protected virtual void Initialize()
+        {
+            // ロガーや処理状態などを初期化、処理時間の測定を開始
+            this.Logger.Clear();
+            this.StatusManager.Clear();
+            this.Stopwatch.Reset();
+            this.Text = String.Empty;
+            this.CancellationPending = false;
+            this.From.WebProxy.Referer = null;
+            this.To.WebProxy.Referer = null;
+            this.Stopwatch.Start();
+        }
+
+        /// <summary>
+        /// 翻訳支援処理実行時の終了処理。
+        /// </summary>
+        /// <remarks>
+        /// <para>
+        /// <see cref="RunBody"/>実行後の後処理のタイミングでコールされる。
+        /// ただし、サーバー接続チェックに失敗した場合などでは、
+        /// <see cref="RunBody"/>が実行されること無く本メソッドが呼ばれる場合もある。
+        /// </para>
+        /// <para>
+        /// オーバーライド時は必ず親のメソッドもコールすること。
+        /// </para>
+        /// </remarks>
+        protected virtual void Terminate()
+        {
+            // 終了後は処理状態やRefererをクリア、処理時間を測定終了
+            this.StatusManager.Clear();
+            this.From.WebProxy.Referer = null;
+            this.To.WebProxy.Referer = null;
+            this.Stopwatch.Stop();
+        }
+
+        #endregion
+
+        #region 実装支援用メソッド
+
+        /// <summary>
         /// ログ出力によるエラー処理を含んだページ取得処理。
         /// </summary>
         /// <param name="title">ページタイトル。</param>
         /// <param name="page">取得したページ。ページが存在しない場合は <c>null</c> を返す。</param>
         /// <returns>処理が成功した(404も含む)場合<c>true</c>、失敗した(通信エラーなど)の場合<c>false</c>。</returns>
-        /// <exception cref="ApplicationException"><see cref="CancellationPending"/>が<c>true</c>の場合。</exception>
+        /// <exception cref="ApplicationException">
+        /// 想定外の例外が発生した場合でかつアプリケーション設定の<c>IgnoreError</c>が<c>false</c>の場合、
+        /// または<see cref="CancellationPending"/>が<c>true</c>の場合。
+        /// </exception>
         /// <remarks>
         /// 本メソッドは、大きく3パターンの動作を行う。
         /// <list type="number">
         /// <item><description>正常にページが取得できた → <c>true</c>でページを設定、ログ出力無し</description></item>
         /// <item><description>404など想定内の例外でページが取得できなかった → <c>true</c>でページ無し、ログ出力無し</description></item>
         /// <item><description>想定外の例外でページが取得できなかった → <c>false</c>でページ無し、ログ出力有り
-        ///                    or <c>ApplicationException</c>で処理中断(アプリケーション設定のIgnoreErrorによる)。</description></item>
+        ///                    or <see cref="ApplicationException"/>で処理中断(アプリケーション設定の<c>IgnoreError</c>による)。</description></item>
         /// </list>
         /// また、実行中は処理状態をサーバー接続中に更新する。
         /// 実行前後には終了要求のチェックも行う。
@@ -329,12 +394,11 @@ namespace Honememo.Wptscs.Logics
             this.ThrowExceptionIfCanceled();
 
             // ページ取得処理、実行中は処理状態を変更
-            bool success = false;
-            Page result = null;
-            this.ChangeStatusInExecuting(
-                () => success = this.TryGetPageBody(title, out result),
-                Resources.StatusDownloading);
-            page = result;
+            bool success;
+            using (var sm = this.StatusManager.Switch(Resources.StatusDownloading))
+            {
+                success = this.TryGetPageBody(title, out page);
+            }
 
             // 通信終了後にも再度終了要求を確認
             this.ThrowExceptionIfCanceled();
@@ -353,101 +417,58 @@ namespace Honememo.Wptscs.Logics
             }
         }
 
-        /// <summary>
-        /// 指定された処理を実行する間、処理状態を渡された値に更新する。
-        /// 処理終了後は以前の処理状態に戻す。
-        /// </summary>
-        /// <param name="method">実行する処理。</param>
-        /// <param name="status">処理状態。</param>
-        protected void ChangeStatusInExecuting(MethodWithChangeStatus method, string status)
-        {
-            // 現在の処理状態を保存、新しい処理状態をセットし、処理を実行する
-            string oldStatus = this.Status;
-            this.Status = status;
-            try
-            {
-                method();
-            }
-            finally
-            {
-                // 処理状態を以前の状態に戻す
-                this.Status = oldStatus;
-            }
-        }
-
         #endregion
 
-        #region privateメソッド
-
-        /// <summary>
-        /// 翻訳支援処理実行時の初期化処理。
-        /// </summary>
-        private void Initialize()
-        {
-            // 変数を初期化
-            this.Logger.Clear();
-            this.Status = String.Empty;
-            this.Stopwatch.Reset();
-            this.Text = String.Empty;
-            this.CancellationPending = false;
-        }
+        #region 内部処理用メソッド
 
         /// <summary>
         /// サーバー接続チェック。
         /// </summary>
         /// <param name="server">サーバー名。</param>
-        /// <returns><c>true</c> 接続成功。</returns>
+        /// <returns>接続成功の場合<c>true</c>。</returns>
         /// <remarks>実行中は処理状態をサーバー接続中に更新する。</remarks>
         private bool Ping(string server)
         {
             // サーバー接続チェック、実行中は処理状態を変更
-            bool result = false;
-            this.ChangeStatusInExecuting(
-                () => result = this.PingBody(server),
-                Resources.StatusPinging);
-            return result;
-        }
-
-        /// <summary>
-        /// サーバー接続チェック本体。
-        /// </summary>
-        /// <param name="server">サーバー名。</param>
-        /// <returns><c>true</c> 接続成功。</returns>
-        private bool PingBody(string server)
-        {
-            // サーバー接続チェック
-            Ping ping = new Ping();
-            try
+            using (var sm = this.StatusManager.Switch(Resources.StatusPinging))
             {
-                PingReply reply = ping.Send(server);
-                if (reply.Status != IPStatus.Success)
+                // サーバー接続チェック
+                Ping ping = new Ping();
+                try
+                {
+                    PingReply reply = ping.Send(server);
+                    if (reply.Status != IPStatus.Success)
+                    {
+                        this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, reply.Status.ToString());
+                        return false;
+                    }
+                }
+                catch (Exception e)
                 {
-                    this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, reply.Status.ToString());
+                    this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, e.InnerException.Message);
                     return false;
                 }
-            }
-            catch (Exception e)
-            {
-                this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, e.InnerException.Message);
-                return false;
-            }
 
-            return true;
+                return true;
+            }
         }
 
         /// <summary>
         /// ログ出力によるエラー処理を含んだページ取得処理本体。
         /// </summary>
         /// <param name="title">ページタイトル。</param>
-        /// <param name="page">取得したページ。ページが存在しない場合は <c>null</c> を返す。</param>
+        /// <param name="page">取得したページ。ページが存在しない場合は<c>null</c>を返す。</param>
         /// <returns>処理が成功した(404も含む)場合<c>true</c>、失敗した(通信エラーなど)の場合<c>false</c>。</returns>
+        /// <exception cref="ApplicationException">
+        /// 想定外の例外が発生した場合でかつアプリケーション設定の<c>IgnoreError</c>が<c>false</c>の場合。
+        /// </exception>
         /// <remarks>
         /// 本メソッドは、大きく3パターンの動作を行う。
         /// <list type="number">
         /// <item><description>正常にページが取得できた → <c>true</c>でページを設定、ログ出力無し</description></item>
         /// <item><description>404など想定内の例外でページが取得できなかった → <c>true</c>でページ無し、ログ出力無し</description></item>
         /// <item><description>想定外の例外でページが取得できなかった → <c>false</c>でページ無し、ログ出力有り
-        ///                    or <c>ApplicationException</c>で処理中断(アプリケーション設定のIgnoreError等による)。</description></item>
+        ///                    or <see cref="ApplicationException"/>で処理中断(アプリケーション設定の<c>IgnoreError</c>による)。</description></item>
         /// </list>
         /// </remarks>
         private bool TryGetPageBody(string title, out Page page)
@@ -464,7 +485,7 @@ namespace Honememo.Wptscs.Logics
                 // ページ無しによる例外も正常終了
                 return true;
             }
-            catch (NotSupportedException)
+            catch (EndPeriodException)
             {
                 // 末尾がピリオドで終わるページが処理できない既知の不具合への対応、警告メッセージを出す
                 this.Logger.AddResponse(Resources.LogMessageErrorPageName, title);
@@ -473,12 +494,7 @@ namespace Honememo.Wptscs.Logics
             catch (Exception e)
             {
                 // その他例外の場合、まずエラー情報を出力
-                this.Logger.AddResponse(e.Message);
-                if (e is WebException && ((WebException)e).Response != null)
-                {
-                    // 出せるならエラーとなったURLも出力
-                    this.Logger.AddResponse(Resources.LogMessageErrorURL, ((WebException)e).Response.ResponseUri);
-                }
+                this.Logger.AddError(e);
 
                 // エラーを無視しない場合、ここで翻訳支援処理を中断する
                 if (!Settings.Default.IgnoreError)
@@ -490,20 +506,6 @@ namespace Honememo.Wptscs.Logics
             }
         }
 
-        /// <summary>
-        /// ロガーのログ状態更新イベント用。
-        /// </summary>
-        /// <param name="sender">イベント発生オブジェクト。</param>
-        /// <param name="e">発生したイベント。</param>
-        private void GetLogUpdate(object sender, EventArgs e)
-        {
-            // もともとこのクラスにあったログ通知イベントをロガーに移動したため、入れ子で呼び出す
-            if (this.LogUpdate != null)
-            {
-                this.LogUpdate(this, EventArgs.Empty);
-            }
-        }
-
         #endregion
     }
 }