// ================================================================================================
// <summary>
-// 翻訳支援処理を実装するための共通クラスソース</summary>
+// 翻訳支援処理を実装するための抽象クラスソース</summary>
//
// <copyright file="Translator.cs" company="honeplusのメモ帳">
// Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
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;
#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>
/// 言語間の項目の対訳表。
{
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);
}
}
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
{
// 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)
{
#endregion
- #region publicメソッド
+ #region 公開メソッド
/// <summary>
/// 翻訳支援処理実行。
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>
/// 翻訳支援処理実行部の本体。
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>
/// また、実行中は処理状態をサーバー接続中に更新する。
/// 実行前後には終了要求のチェックも行う。
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();
}
}
- /// <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
{
- this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, reply.Status.ToString());
+ 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, 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)
// ページ無しによる例外も正常終了
return true;
}
- catch (NotSupportedException)
+ catch (EndPeriodException)
{
// 末尾がピリオドで終わるページが処理できない既知の不具合への対応、警告メッセージを出す
this.Logger.AddResponse(Resources.LogMessageErrorPageName, title);
}
}
- /// <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
}
}