// Copyright (c) 2008, NTT DATA Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Text;
using System.Threading;
using TERASOLUNA.Fw.Client.Conversion;
using TERASOLUNA.Fw.Client.Forms;
using TERASOLUNA.Fw.Common;
using TERASOLUNA.Fw.Common.BLogic;
using TERASOLUNA.Fw.Common.Logging;
using TERASOLUNA.Fw.Common.Validation;

namespace TERASOLUNA.Fw.Client
{
    /// <summary>
    /// 指定されたビジネスロジックを実行するクラスです。
    /// </summary>
    /// <remarks>
    /// 指定されたビジネスロジックを同期もしくは非同期で実行します。
    /// </remarks>
    public class Coordinator
    {
        /// <summary>
        /// <see cref="ILog"/> 実装クラスのインスタンスです。
        /// </summary>
        /// <remarks>
        /// ログ出力に利用します。
        /// </remarks>
        private static ILog _log = LogFactory.GetLogger(typeof(Coordinator));

        /// <summary>
        /// アプリケーション構成ファイルの appSettings 要素に指定される 
        /// <see cref="Coordinator"/> の key 名です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "CoordinatorTypeName" です。
        /// </remarks>
        public static readonly string COORDINATOR_TYPENAME = "CoordinatorTypeName";

        /// <summary>
        /// アプリケーション構成ファイルの appSettings 要素に指定される 
        /// <see cref="IValidator"/> の key 名です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "ValidatorTypeName" です。
        /// </remarks>
        public static readonly string VALIDATOR_TYPENAME = "ValidatorTypeName";

        /// <summary>
        /// アプリケーション構成ファイルの appSettings 要素に指定される 
        /// <see cref="IConverter"/> の key 名です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "ConverterTypeName" です。
        /// </remarks>
        public static readonly string CONVERTER_TYPENAME = "ConverterTypeName";

        /// <summary>
        /// 入力値検証エラー時の結果文字列です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "validationFailed" です。
        /// </remarks>
        public static readonly string VALIDATION_FAILED = "validationFailed";

        /// <summary>
        /// 前処理エラー時の結果文字列です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "preprocessedFailed" です。
        /// </remarks>
        public static readonly string PREPROCESSED_FAILED = "preprocessedFailed";

        /// <summary>
        /// <see cref="Coordinator"/> 派生クラスのタイプです。
        /// </summary>
        private static Type _coordinatorType = null;

        /// <summary>
        /// <see cref="EventController"/> を保持します。
        /// </summary>
        private EventController _eventController = null;

        /// <summary>
        /// <see cref="IValidator"/> 実装クラスのインスタンスを保持します。
        /// </summary>
        private IValidator _validator = null;

        /// <summary>
        /// <see cref="IConverter"/> 実装クラスのインスタンスを保持します。
        /// </summary>
        private IConverter _converter = null;

        /// <summary>
        /// <see cref="AsyncOperation"/> インスタンスを保持します。
        /// </summary>
        private AsyncOperation _asyncOpe = null;

        /// <summary>
        /// <see cref="IBLogic"/> 実装クラスのインスタンスを保持します。
        /// </summary>
        private IBLogic _blogic = null;

        /// <summary>
        /// ビジネスロジックが非同期で実行中かどうかを表します。
        /// </summary>
        /// <remarks>
        /// <para><see cref="CoordinatorType"/> が非同期操作を実行中の場合は true になります。
        /// それ以外の場合は false になります。 </para>
        /// </remarks>
        private bool _isBusy = false;

        /// <summary>
        /// ビジネスロジックを別スレッドで実行するデリゲートインスタンスです。
        /// </summary>
        private DoExecuteAsyncDelegate _doExecuteAsyncDelegate;

        /// <summary>
        /// 非同期処理が完了したときに実行されるコールバックデリゲートインスタンスです。
        /// </summary>
        private SendOrPostCallback _executeAsyncCompletedCallback;

        /// <summary>
        /// 非同期処理から進行状況を報告するときに実行されるコールバックデリゲートインスタンスです。
        /// </summary>
        private SendOrPostCallback _executeAsyncProgressChangedCallback;

        /// <summary>
        /// <see cref="Coordinator"/> クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <remarks>
        /// デリゲート、コールバックデリゲートを初期化します。
        /// </remarks>
        public Coordinator()
        {
            _doExecuteAsyncDelegate = new DoExecuteAsyncDelegate(DoExecuteAsync);
            _executeAsyncCompletedCallback =
                new SendOrPostCallback(ExecuteAsyncCompletedCallback);
            _executeAsyncProgressChangedCallback =
                new SendOrPostCallback(ExecuteAsyncProgressChangedCallback);
        }

        /// <summary>
        /// <see cref="Coordinator"/> 派生クラスのタイプを取得または設定します。
        /// </summary>
        /// <value>
        /// <see cref="Coordinator"/> 派生クラスのタイプ。
        /// </value>
        protected static Type CoordinatorType
        {
            get
            {
                return _coordinatorType;
            }
            set
            {
                _coordinatorType = value;
            }
        }

        /// <summary>
        /// <see cref="EventController"/> を取得または設定します。
        /// </summary>
        /// <value>
        /// <see cref="Coordinator"/> の呼び出し元である <see cref="EventController"/> のインスタンス。
        /// </value>
        protected EventController EventController
        {
            get
            {
                return _eventController;
            }
            set
            {
                _eventController = value;
            }
        }

        /// <summary>
        /// <see cref="IValidator"/> 実装クラスのインスタンスを取得または設定します。
        /// </summary>
        /// <value>
        /// <see cref="IValidator"/> 実装クラスのインスタンス。
        /// </value>
        protected IValidator Validator
        {
            get
            {
                return _validator;
            }
            set
            {
                _validator = value;
            }
        }

        /// <summary>
        /// <see cref="IConverter"/> 実装クラスのインスタンスを取得または設定します。
        /// </summary>
        /// <value>
        /// <see cref="IConverter"/> 実装クラスのインスタンス。
        /// </value>
        protected IConverter Converter
        {
            get
            {
                return _converter;
            }
            set
            {
                _converter = value;
            }
        }

        /// <summary>
        /// <see cref="AsyncOperation"/> インスタンスを取得または設定します。
        /// </summary>
        /// <value>
        /// 非同期処理の進行状況を追跡するオブジェクト。
        /// </value>
        protected AsyncOperation AsyncOpe
        {
            get
            {
                return _asyncOpe;
            }
            set
            {
                _asyncOpe = value;
            }
        }

        /// <summary>
        /// <see cref="IBLogic"/> 実装クラスを取得または設定します。
        /// </summary>
        /// <value>
        /// <see cref="IBLogic"/> 実装クラス。
        /// </value>
        protected IBLogic BLogic
        {
            get
            {
                return _blogic;
            }
            set
            {
                _blogic = value;
            }
        }

        /// <summary>
        /// <see cref="Coordinator"/> が非同期処理を実行中かどうかを示す値を取得します。 
        /// </summary>
        /// <value>
        /// <see cref="Coordinator"/> が非同期処理を実行中の場合は true。それ以外の場合は false。 
        /// </value>
        public bool IsBusy
        {
            get
            {
                return _isBusy;
            }
            protected set
            {
                _isBusy = value;
            }
        }

        /// <summary>
        /// <see cref="IValidator"/> 実装クラスと 
        /// <see cref="IConverter"/> 実装クラスのインスタンスを生成します。
        /// </summary>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <see cref="IValidator"/> 実装クラスのインスタンスを生成できません。
        /// </item>
        /// <item>
        /// <see cref="IConverter"/> 実装クラスのインスタンスを生成できません。
        /// </item>
        /// </list>
        /// </exception>
        protected virtual void Initialize()
        {
            // 入力値検証クラスのインスタンスの生成
            _validator = ValidatorFactory.CreateValidator();
            // データセット変換クラスのインスタンスの生成
            _converter = ConverterFactory.CreateConverter();
        }

        /// <summary>
        /// <see cref="Coordinator"/> クラスのインスタンスまたは、
        /// <see cref="Coordinator"/> 派生クラスのインスタンスを生成し、返却します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// アプリケーション構成ファイルに設定された 
        /// <see cref="Coordinator"/> 派生クラスのインスタンスを生成し、返却します。
        /// </para>
        /// <para>
        /// アプリケーション構成ファイルに
        /// <see cref="Coordinator"/> 派生クラスの設定がない場合は、 
        /// <see cref="Coordinator"/> のインスタンスを返却します。
        /// </para>
        /// </remarks>
        /// <returns>
        /// <see cref="Coordinator"/> クラスのインスタンス。
        /// または、<see cref="Coordinator"/> 派生クラスのインスタンス。
        /// </returns>
        /// <exception cref="TerasolunaException">
        /// <see cref="Coordinator"/> 派生クラスのインスタンスを生成できません。
        /// </exception>
        public static Coordinator CreateCoordinator()
        {
            Coordinator coordinator = null;

            // Coordinatorを生成及び型をキャッシュ
            if (_coordinatorType == null)
            {
                coordinator = ClassUtil.CreateInstanceFromAppSettings<Coordinator>(
                    COORDINATOR_TYPENAME, typeof(Coordinator));
                _coordinatorType = coordinator.GetType();
            }
            else
            {
                coordinator =
                    ClassUtil.CreateInstanceByType<Coordinator>(_coordinatorType);
            }

            return coordinator;
        }

        /// <summary>
        /// ビジネスロジックの実行に必要な情報を格納する
        /// <see cref="BLogicParam"/> クラスのインスタンスを生成し、返却します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// <see cref="EventController"/> に設定したビジネスロジック入力 <see cref="DataSet"/> の
        /// 型名をもとにインスタンスを生成します。
        /// </para>
        /// <para>
        /// <see cref="EventController"/> にビジネスロジック入力 <see cref="DataSet"/> の型名が
        /// 設定されていない場合は、<see cref="Coordinator.EventController"/> の ViewData と
        /// 同じ型の <see cref="DataSet"/> が生成されます。
        /// <see cref="Coordinator.EventController"/> の ViewData も設定されていない場合は、
        /// 空の <see cref="DataSet"/> が生成されます。
        /// </para>
        /// <para>
        /// <see cref="BLogicParam"/> クラスのインスタンスを生成し、
        /// <see cref="EventController"/> の Items を、生成したインスタンスの Items にコピーします。
        /// また、<see cref="Client.EventController.REQUEST_NAME"/> をキーとして <see cref="Client.EventController.RequestName"/> の 
        /// の値を Items に追加します。<see cref="Client.EventController.REQUEST_NAME"/> がキーとしてすでに利用されていた場合、
        /// 値を上書きします。
        /// </para>
        /// <para>
        /// <see cref="BLogicParam"/> クラスのインスタンスの <see cref="BLogicParam.ParamData"/> に
        /// 生成したビジネスロジック入力 <see cref="DataSet"/> のインスタンスを設定します。
        /// </para>
        /// </remarks>
        /// <returns>
        /// ビジネスロジックの実行に必要な情報を格納するためのクラス <see cref="BLogicParam"/>。
        /// </returns>
        /// <param name="blogicParamTypeName">
        /// ビジネスロジック入力 <see cref="DataSet"/> の型名。
        /// </param>
        /// <param name="viewData">
        /// 画面 <see cref="DataSet"/>。
        /// </param>
        /// <param name="items">
        /// ビジネスロジックの実行に必要なパラメータを格納する <see cref="IDictionary{String,Object}"/>。
        /// </param>
        /// <param name="requestName">
        /// 通信相手に通知するリクエスト名。
        /// </param>
        /// <exception cref="TerasolunaException">
        /// <see cref="DataSet"/> のインスタンスを生成できません。
        /// </exception>
        protected virtual BLogicParam CreateBLogicParam(
            string blogicParamTypeName, DataSet viewData, IDictionary<string, object> items, string requestName)
        {
            // ビジネスロジック入力データセット
            DataSet blogicParamData = null;

            // typeNameがnullまたは空文字の場合
            if (string.IsNullOrEmpty(blogicParamTypeName))
            {
                if (viewData == null)
                {
                    blogicParamData = ClassUtil.CreateInstanceByType<DataSet>(typeof(DataSet));
                }
                else
                {
                    blogicParamData = ClassUtil.CreateInstanceByType<DataSet>(viewData.GetType());
                }
            }
            else
            {
                blogicParamData = ClassUtil.CreateInstanceByTypeName<DataSet>(blogicParamTypeName);
            }

            // 入力オブジェクトの生成
            BLogicParam blogicParam = new BLogicParam();

            // EventControllerのItemsからBLogicParamのItemsへのコピー
            foreach (string key in items.Keys)
            {
                blogicParam.Items.Add(key, items[key]);
            }

            // Request名の設定
            if (!string.IsNullOrEmpty(requestName))
            {
                blogicParam.Items[EventController.REQUEST_NAME] = requestName;
            }

            if (_log.IsTraceEnabled)
            {
                StringBuilder message = new StringBuilder();
                if (blogicParam.Items.Count > 0)
                {
                    StringBuilder itemList = new StringBuilder();
                    foreach (string key in blogicParam.Items.Keys)
                    {
                        itemList.AppendLine(string.Format(
                            Properties.Resources.T_DICTIONARY_KEY_VALUE, key, blogicParam.Items[key]));
                    }
                    message.AppendLine(string.Format(
                        Properties.Resources.T_ITEMS_VALUE, blogicParam.GetType().FullName, "Items"));
                    message.Append(itemList);
                }
                else
                {
                    message.AppendLine(string.Format(
                        Properties.Resources.T_NOT_ITEMS_VALUE, blogicParam.GetType().FullName, "Items"));
                }
                _log.Trace(message.ToString().Trim());
            }

            blogicParam.ParamData = blogicParamData;

            return blogicParam;
        }

        /// <summary>
        /// ビジネスロジック出力 <see cref="DataSet"/> の型名とビジネスロジック名を指定し、
        /// <see cref="IBLogic"/> 実装クラスのインスタンスを生成して返却します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// <paramref name="eventController"/> に設定した <see cref="Client.EventController.BLogicName"/> 、
        /// ビジネスロジック出力 <see cref="DataSet"/> の型名をもとに
        /// インスタンスを生成して返却します。
        /// </para>
        /// <para>
        /// <see cref="Client.EventController.BLogicName"/> が null または空文字列の場合は、
        /// 何も処理を行わない <see cref="NopBLogic"/> のインスタンスを生成して返却します。
        /// </para>
        /// <para>
        /// ビジネスロジックでジェネリックの型パラメータを必要としている場合は、
        /// <see cref="Coordinator.EventController"/> の <see cref="ExecutionResult.BLogicResultData"/> に 
        /// <see cref="DataSet"/> の型名を指定してください。
        /// </para>
        /// <para>
        /// <see cref="Coordinator.EventController"/> の 
        /// <see cref="Client.EventController.BLogicResultTypeName"/> に 
        /// <see cref="DataSet"/> の型名が指定されていない場合は、
        /// <see cref="Coordinator.EventController"/> の 
        /// <see cref="Client.EventController.ViewData"/> の型名を利用します。
        /// <see cref="Coordinator.EventController"/> の 
        /// <see cref="Client.EventController.ViewData"/> も設定されていない場合は、
        /// 空の <see cref="DataSet"/> の型名を利用します。
        /// </para>
        /// </remarks>
        /// <param name="blogicName">ビジネスロジック名。</param>
        /// <param name="blogicResultTypeName">ビジネスロジック出力 <see cref="DataSet"/> の型名。</param>
        /// <param name="viewData">画面 <see cref="DataSet"/>。</param>
        /// <returns>
        /// <see cref="IBLogic"/> 実装クラスのインスタンス。
        /// </returns>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <description>
        /// <see cref="IBLogic"/> 実装クラスのインスタンスが生成できません。
        /// </description>
        /// </item>
        /// <item>
        /// <description>コンストラクタにアクセスできない場合。</description>
        /// </item>
        /// <item>
        /// <description>コンストラクタで例外が発生した場合。</description>
        /// </item>
        /// </list>
        /// </exception>
        protected virtual IBLogic CreateBLogic(string blogicName, string blogicResultTypeName, DataSet viewData)
        {
            // イベントコンテキストからビジネスロジックでの処理結果を格納するDataSetのタイプ名を取得
            // タイプ名が指定されていない場合は、ViewDataの型をBLogicFactoryに渡す
            string genericTypeName = null;
            if (string.IsNullOrEmpty(blogicResultTypeName))
            {
                if (viewData == null)
                {
                    genericTypeName = typeof(DataSet).AssemblyQualifiedName;
                }
                else
                {
                    genericTypeName = viewData.GetType().AssemblyQualifiedName;
                }
            }
            else
            {
                genericTypeName = blogicResultTypeName;
            }

            IBLogic blogic = BLogicFactory.CreateBLogic(blogicName, genericTypeName);

            IProgressChangedEventInvoker progressChangedEventInvoker = blogic as IProgressChangedEventInvoker;
            if (progressChangedEventInvoker != null)
            {
                progressChangedEventInvoker.ProgressChanged
                    += new ExecuteProgressChangedEventHandler(DoExecuteProgressChanged);
            }

            return blogic;
        }

        /// <summary>
        /// イベント処理結果を格納する <see cref="ExecutionResult"/> を生成します。
        /// </summary>
        /// <returns>
        /// 生成した <see cref="ExecutionResult"/> のインスタンス。
        /// </returns>
        protected virtual ExecutionResult CreateExecutionResult()
        {
            return new ExecutionResult();
        }

        /// <summary>
        /// 入力値検証結果を格納する <see cref="ValidationResult"/> を生成します。
        /// </summary>
        /// <returns>
        /// 生成した <see cref="ValidationResult"/> のインスタンス。
        /// </returns>
        protected virtual ValidationResult CreateValidationResult()
        {
            return new ValidationResult();
        }

        /// <summary>
        /// ビジネスロジックを同期処理で実行し、
        /// 実行結果の <see cref="ExecutionResult"/> を返却します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// ビジネスロジックを同期処理で実行し、
        /// 実行結果の <see cref="ExecutionResult"/> を返却します。
        /// </para>
        /// <para>
        /// 画面 <see cref="DataSet"/> に対して入力値検証を行います。
        /// 入力値検証は、入力値検証機能による単項目チェックと、
        /// <see cref="EventController"/> のカスタムチェックイベントで行うことできます。
        /// 入力値検証失敗時には同期処理は実行されず、
        /// <see cref="ExecutionResult"/> の結果文字列に
        /// <see cref="Coordinator.VALIDATION_FAILED"/> が格納され、
        /// <see cref="FormBase"/> のエラーハンドラ及び、
        /// <see cref="EventController"/> の入力値検証失敗イベントを実行し、
        /// <see cref="ExecutionResult"/> が返却されます。
        /// また、入力値検証失敗イベントで結果文字列を変更することができます。
        /// </para>
        /// <para>
        /// 画面 <see cref="DataSet"/> の内容を、 <see cref="EventController"/> で設定されている
        /// ビジネスロジック入力 <see cref="DataSet"/> に詰め替えます。
        /// </para>
        /// <para>
        /// ビジネスロジック入力 <see cref="DataSet"/> に対して前処理を行います。
        /// 前処理は、 <see cref="EventController"/> の前処理イベントで行うことができます。
        /// 前処理失敗時には同期処理は実行されず、
        /// <see cref="ExecutionResult"/> の結果文字列に
        /// <see cref="Coordinator.PREPROCESSED_FAILED"/> 、または前処理イベントで設定した任意の結果文字列が格納され、
        /// <see cref="FormBase"/> のエラーハンドラ及び、
        /// <see cref="EventController"/> の前処理失敗イベントを実行し、
        /// <see cref="ExecutionResult"/> が返却されます。
        /// また、前処理失敗イベントで、結果文字列を変更することができます。
        /// </para>
        /// <para>
        /// ビジネスロジックを実行します。
        /// <see cref="ExecutionResult.ResultString"/> が <see cref="BLogicResult.SUCCESS"/> の場合、
        /// ビジネスロジックの結果 <see cref="DataSet"/> を画面 <see cref="DataSet"/> に変換を行い、
        /// <see cref="ExecutionResult"/> を返却します。
        /// <see cref="ExecutionResult.ResultString"/> が <see cref="BLogicResult.SUCCESS"/> 以外の場合、
        /// <see cref="FormBase"/> のエラーハンドラ及び、
        /// <see cref="EventController"/> のイベント処理失敗イベントを実行し、
        /// <see cref="ExecutionResult"/> を返却します。
        /// </para>
        /// </remarks>
        /// <param name="eventController">
        /// <see cref="Execute"/> メソッドの呼び出し元である <see cref="EventController"/> のインスタンス。
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="eventController"/> が null 参照です。
        /// </exception>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">      
        /// <item>
        /// <description>
        /// <see cref="Coordinator.EventController"/> の ValidationFilePath を設定したが、
        /// <see cref="Coordinator.EventController"/> の View が null 参照の場合。
        /// </description>
        /// </item>        
        /// <item>
        /// <description>
        /// <see cref="Coordinator.EventController"/> の ConvertId を設定したが、
        /// <see cref="Coordinator.EventController"/> の View が null 参照の場合。
        /// </description>
        /// </item>    
        /// <item>
        /// <description>
        /// <see cref="Coordinator.EventController"/> の ConvertId を設定したが、
        /// <see cref="Coordinator.BLogic"/> の <see cref="IBLogic.Execute"/> 
        /// 実行結果である <see cref="BLogicResult"/> の ResultData が null 参照の場合。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// <see cref="IValidator"/> 実装クラスのインスタンスを生成できません。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// <see cref="IConverter"/> 実装クラスのインスタンスを生成できません。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// ビジネスロジック入力 <see cref="DataSet"/> のインスタンスを生成できません。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// <see cref="IBLogic"/> 実装クラスのインスタンスを生成できません。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// ビジネスロジックからの戻り値 <see cref="BLogicResult"/> が null 参照です。
        /// </description>
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="ConversionException">
        /// データセット変換中にエラーが発生しました。
        /// </exception>
        /// <returns>
        /// 実行結果を格納する <see cref="ExecutionResult"/> のインスタンス。
        /// </returns>
        public virtual ExecutionResult Execute(EventController eventController)
        {
            if (eventController == null)
            {
                ArgumentNullException exception = new ArgumentNullException("eventController");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(
                        Properties.Resources.E_NULL_ARGUMENT, "eventController"), exception);
                }
                throw exception;
            }

            _eventController = eventController;

            // イベント処理結果クラスの生成
            ExecutionResult executionResult = CreateExecutionResult();

            // エラークリア
            ClearError(eventController.ErrorHandler, eventController.ViewData, eventController.ErrorPaths);

            // Validator, Converterの初期化
            Initialize();

            // 前処理を行い、実行するBLogicの入力パラメータを取得する。
            BLogicParam blogicParam = Preprocess(eventController, executionResult);
            if (!string.IsNullOrEmpty(executionResult.ResultString))
            {
                return executionResult;
            }

            // BLogic実行(同期)
            _blogic = CreateBLogic(
                eventController.BLogicName, eventController.BLogicResultTypeName, eventController.ViewData);
            BLogicResult blogicResult = null;
            try
            {
                blogicResult = ExecuteBLogic(blogicParam);
            }
            catch (Exception e)
            {
                if (_log.IsErrorEnabled)
                {
                    _log.Error(Properties.Resources.E_BLOGIC_EXECUTE_EXCEPTION, e);
                }
                throw;
            }

            if (blogicResult == null)
            {
                TerasolunaException exception = new TerasolunaException(
                    Properties.Resources.E_BLOGIC_NULL_RESULT);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            // ビジネスロジックの後処理
            Postprocess(eventController, executionResult, blogicResult);

            return executionResult;
        }

        /// <summary>
        /// ビジネスロジックの前処理を行い、ビジネスロジック入力 <see cref="DataSet"/> を取得します。
        /// </summary>
        /// <param name="eventController"><see cref="EventController"/> のインスタンス。</param>
        /// <param name="executionResult"><see cref="Coordinator"/> の実行結果を格納した 
        /// <see cref="ExecutionResult"/> のインスタンス。</param>
        /// <returns>ビジネスロジック入力 <see cref="DataSet"/> のインスタンス。</returns>
        protected virtual BLogicParam Preprocess(
            EventController eventController, ExecutionResult executionResult)
        {
            // ビジネスロジック入力データセット
            BLogicParam blogicParam = null;

            // 入力値検証実行
            ValidationResult validationResult = Validate(eventController, executionResult);
            if (!validationResult.IsValid)
            {
                return blogicParam;
            }

            // ビジネスロジック入力データセット生成
            blogicParam = CreateBLogicParam(eventController.BLogicParamTypeName,
                eventController.ViewData, eventController.Items, eventController.RequestName);

            // ViewData→BLogicParamData変換
            ConvertDataSet(eventController.ConvertId, eventController.ViewData, blogicParam.ParamData);

            // 前処理完了イベント
            // イベント処理でエラーがあった場合は Coordinator を終了
            eventController.ReportPreprocessed(executionResult, blogicParam.ParamData);
            if (!string.IsNullOrEmpty(executionResult.ResultString) || executionResult.Errors.Count > 0)
            {
                if (_log.IsDebugEnabled)
                {
                    _log.Debug(Properties.Resources.D_PREPROCESSED_FAILED);
                }
                if (string.IsNullOrEmpty(executionResult.ResultString))
                {
                    executionResult.ResultString = PREPROCESSED_FAILED;
                }
                HandleError(eventController.ErrorHandler, executionResult, 
                    eventController.ViewData, eventController.ErrorPaths);
                eventController.ReportPreprocessedFailed(executionResult, blogicParam.ParamData);
            }

            return blogicParam;
        }

        /// <summary>
        /// 入力値検証を行います。
        /// </summary>
        /// <remarks>
        /// <para>
        /// <see cref="Client.EventController.ViewData"/> に対して入力値検証を行い、
        /// <see cref="Client.EventController"/> のカスタムチェックイベントを実行します。
        /// </para>
        /// </remarks>
        /// <param name="eventController"><see cref="Client.EventController"/> のインスタンス。</param>
        /// <param name="executionResult">実行結果を格納する <see cref="ExecutionResult"/> のインスタンス。</param>
        /// <returns>
        /// 検証結果のエラーを保持した <see cref="ValidationResult"/> のインスタンス。
        /// </returns>
        protected virtual ValidationResult Validate(
            EventController eventController, ExecutionResult executionResult)
        {
            // 入力値検証機能によるデータセットに対する入力値検証
            ValidationResult validationResult = 
                ValidateViewData(eventController.ViewData, eventController.ValidationFilePath, eventController.RuleSet);

            // カスタムチェックイベントによる入力値検証
            eventController.ReportCustomValidating(validationResult);

            // 入力値検証エラー時の処理
            if (!validationResult.IsValid)
            {
                if (_log.IsDebugEnabled)
                {
                    _log.Debug(Properties.Resources.D_VALIDATOR_FAILED);
                }
                RepackExecutionResult(validationResult, executionResult);
                executionResult.ResultString = VALIDATION_FAILED;
                HandleError(eventController.ErrorHandler, executionResult, 
                    eventController.ViewData, eventController.ErrorPaths);
                eventController.ReportValidationFailed(executionResult);
            }

            return validationResult;
        }

        /// <summary>
        /// ビジネスロジックの処理結果を処理します。
        /// </summary>
        /// <remarks>
        /// ビジネスロジック成功の場合は、ビジネスロジック出力データセットの値を、
        /// 画面データセットにデータセット変換を行う。
        /// ビジネスロジック失敗の場合は、エラー処理機能を呼びだし後、
        /// ビジネスロジック失敗イベントを発行する。
        /// </remarks>
        /// <param name="eventController">
        /// <see cref="Execute"/> メソッドの呼び出し元または、
        /// <see cref="ExecuteAsync"/> メソッドの呼び出し元である 
        /// <see cref="EventController"/> のインスタンス。
        /// </param>
        /// <param name="executionResult">
        /// 実行結果を格納する <see cref="ExecutionResult"/> のインスタンス。
        /// </param>
        /// <param name="blogicResult">
        /// ビジネスロジックの実行結果を格納する <see cref="BLogicResult"/> のインスタンス。
        /// </param>
        protected virtual void Postprocess(
            EventController eventController, 
            ExecutionResult executionResult, 
            BLogicResult blogicResult)
        {
            // BLogicResultからExecutionResultへの詰め替え
            RepackExecutionResult(blogicResult, executionResult);

            // BLogicResultからEventControllerへItemsの詰め替え
            foreach (string key in blogicResult.Items.Keys)
            {
                eventController.Items[key] = blogicResult.Items[key];
            }

            if (_log.IsTraceEnabled)
            {
                if (eventController.Items.Count != 0)
                {
                    StringBuilder message = new StringBuilder();
                    StringBuilder itemList = new StringBuilder();
                    foreach (string key in eventController.Items.Keys)
                    {
                        itemList.AppendLine(string.Format(
                            Properties.Resources.T_DICTIONARY_KEY_VALUE,
                            key, eventController.Items[key]));
                    }
                    message.AppendLine(string.Format(
                        Properties.Resources.T_ITEMS_VALUE, eventController.GetType().FullName, "Items"));
                    message.Append(itemList);
                    _log.Trace(message.ToString().Trim());
                }
                else
                {
                    _log.Trace(string.Format(
                        Properties.Resources.T_NOT_ITEMS_VALUE, eventController.GetType().FullName, "Items"));
                }
            }

            // ビジネスロジック成功
            if (BLogicResult.SUCCESS.Equals(executionResult.ResultString))
            {
                ReverseDataSet(eventController.ConvertId, eventController.ViewData, executionResult.BLogicResultData);
            }
            else
            {
                // ビジネスロジック失敗
                HandleError(eventController.ErrorHandler, executionResult, 
                    eventController.ViewData, eventController.ErrorPaths);
                eventController.ReportBLogicFailed(executionResult);
            }
        }

        /// <summary>
        /// ビジネスロジックを実行して、<see cref="BLogicResult"/> のインスタンスを返却します。
        /// </summary>
        /// <param name="blogicParam">
        /// ビジネスロジック入力 <see cref="BLogicParam"/> のインスタンス。
        /// </param>
        /// <returns>
        /// ビジネスロジック実行結果を格納する <see cref="BLogicResult"/> のインスタンス。
        /// </returns>
        protected virtual BLogicResult ExecuteBLogic(BLogicParam blogicParam)
        {
            if (_log.IsDebugEnabled)
            {
                _log.Debug(string.Format(
                    Properties.Resources.D_BEGIN_BLOGIC_FUNCTION,
                    _blogic.GetType().FullName));
            }

            BLogicResult blogicResult = _blogic.Execute(blogicParam);

            if (_log.IsDebugEnabled)
            {
                _log.Debug(string.Format(
                    Properties.Resources.D_END_BLOGIC_FUNCTION,
                    _blogic.GetType().FullName));
            }

            return blogicResult;
        }

        /// <summary>
        /// <see cref="Client.EventController.ViewData"/> に対して入力値検証を行い、
        /// <see cref="ValidationResult"/> のインスタンスを返却します。
        /// </summary>
        /// <param name="viewData">画面 <see cref="DataSet"/>。</param>
        /// <param name="validationFilePath">入力値検証設定ファイルパス。</param>
        /// <param name="ruleSet">入力値検証ルールセット名。</param>
        /// <remarks>
        /// <para>
        /// <see cref="Client.EventController.ValidationFilePath"/> が設定されていれば、
        /// 入力値検証機能を利用した単項目チェックを実行します。
        /// </para>
        /// </remarks>
        /// <returns>
        /// 入力値検証結果を格納する <see cref="ValidationResult"/> のインスタンス。
        /// </returns>
        /// <exception cref="TerasolunaException">
        /// <see cref="Client.EventController.ValidationFilePath"/> を設定したが、
        /// <see cref="Client.EventController.ViewData"/> が null 参照の場合。
        /// </exception>
        protected virtual ValidationResult ValidateViewData(DataSet viewData, string validationFilePath, string ruleSet)
        {
            ValidationResult validationResult = CreateValidationResult();

            // 入力値検証機能による単項目チェック
            if (!string.IsNullOrEmpty(validationFilePath))
            {
                if (viewData == null)
                {
                    string message = string.Format(
                        Properties.Resources.E_BLOGIC_NULL_DATASET,
                        "ValidationFilePath",
                        "ViewData");
                    TerasolunaException exception = new TerasolunaException(message);
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(message, exception);
                    }
                    throw exception;
                }

                _validator.ValidationFilePath = validationFilePath;
                if (!string.IsNullOrEmpty(ruleSet))
                {
                    _validator.RuleSet = ruleSet;
                }

                if (_log.IsDebugEnabled)
                {
                    _log.Debug(string.Format(Properties.Resources.D_CALL_VALIDATOR,
                        validationFilePath, ruleSet));
                }

                validationResult = _validator.Validate(viewData);
            }
            else
            {
                if (_log.IsDebugEnabled)
                {
                    _log.Debug(Properties.Resources.D_NOT_VALIDATE_VIEW_DATA);
                }
            }

            return validationResult;
        }

        /// <summary>
        /// <see cref="IConverter"/> の <see cref="IConverter.Convert"/> を実行します。
        /// </summary>
        /// <param name="convertId">コンバート ID。</param>
        /// <param name="viewData">画面 <see cref="DataSet"/>。</param>
        /// <param name="blogicParamData">
        /// 変換先 <see cref="DataSet"/> のインスタンス。
        /// </param>
        /// <exception cref="TerasolunaException">
        /// <see cref="EventController"/> の ConvertId を設定したが、
        /// <see cref="EventController"/> の ViewData が null 参照の場合。
        /// </exception>
        /// <exception cref="ConversionException">
        /// データセット変換中にエラーが発生しました。
        /// </exception>
        protected virtual void ConvertDataSet(string convertId, DataSet viewData, DataSet blogicParamData)
        {
            if (!string.IsNullOrEmpty(convertId))
            {
                if (viewData == null)
                {
                    string message = string.Format(
                        Properties.Resources.E_BLOGIC_NULL_DATASET, "ConvertId", "ViewData");
                    TerasolunaException exception = new TerasolunaException(message);
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(message, exception);
                    }
                    throw exception;
                }

                if (_log.IsDebugEnabled)
                {
                    _log.Debug(string.Format(Properties.Resources.D_CALL_CONVERTER, convertId, "Convert"));
                }

                _converter.Convert(convertId, viewData, blogicParamData);
            }
            else
            {
                if (_log.IsDebugEnabled)
                {
                    _log.Debug(string.Format(Properties.Resources.D_NOT_CONVERT_DATA_SET, "Convert"));
                }
            }
        }

        /// <summary>
        /// ビジネスロジックの実行結果が格納された <see cref="BLogicResult"/> インスタンスを、 
        /// <see cref="Coordinator"/> の実行結果を格納する 
        /// <see cref="ExecutionResult"/> インスタンスへ詰め替えます。
        /// </summary>
        /// <param name="blogicResult">
        /// ビジネスロジックの実行結果が格納された <see cref="BLogicResult"/> のインスタンス。
        /// </param>
        /// <param name="executionResult">
        /// <see cref="Coordinator"/> の実行結果を格納する 
        /// <see cref="ExecutionResult"/> のインスタンス。
        /// </param>
        protected virtual void RepackExecutionResult(
            BLogicResult blogicResult, ExecutionResult executionResult)
        {
            executionResult.AddErrors(blogicResult.Errors);
            executionResult.BLogicResultData = blogicResult.ResultData;
            executionResult.ResultString = blogicResult.ResultString;
        }

        /// <summary>
        /// 入力値検証結果が格納された <see cref="ValidationResult"/> インスタンスを 
        /// <see cref="Coordinator"/> の実行結果を格納する 
        /// <see cref="ExecutionResult"/> インスタンスへ詰め替えます。
        /// </summary>
        /// <param name="validationResult">
        /// 入力値検証の結果が格納された <see cref="ValidationResult"/> のインスタンス。
        /// </param>
        /// <param name="executionResult">
        /// <see cref="Coordinator"/> の実行結果を格納する <see cref="ExecutionResult"/> のインスタンス。
        /// </param>
        protected virtual void RepackExecutionResult(
            ValidationResult validationResult, ExecutionResult executionResult)
        {
            foreach (MessageInfo message in validationResult.Errors)
            {
                executionResult.Errors.Add(message);
            }
        }

        /// <summary>
        /// <see cref="IConverter"/> の <see cref="IConverter.Reverse"/> を実行します。
        /// </summary>
        /// <param name="convertId">コンバート ID。</param>
        /// <param name="viewData">画面 <see cref="DataSet"/>。</param>
        /// <param name="blogicResultData">ビジネスロジックの実行結果を格納する <see cref="DataSet"/>。</param>
        /// <exception cref="TerasolunaException">
        /// <see cref="EventController"/> の ConvertId を設定したが、
        /// <see cref="Coordinator.BLogic"/> の <see cref="IBLogic.Execute"/> 
        /// 実行結果である <see cref="BLogicResult"/> の BLogicResultData が null 参照の場合。
        /// </exception>
        protected virtual void ReverseDataSet(
            string convertId, DataSet viewData, DataSet blogicResultData)
        {
            if (!string.IsNullOrEmpty(convertId))
            {
                if (blogicResultData == null)
                {
                    TerasolunaException exception = new TerasolunaException(Properties.Resources.E_BLOGIC_RESULT_NULL_DATASET);
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(exception.Message, exception);
                    }
                    throw exception;
                }

                if (_log.IsDebugEnabled)
                {
                    _log.Debug(string.Format(Properties.Resources.D_CALL_CONVERTER, convertId, "Reverse"));
                }

                _converter.Reverse(convertId, viewData, blogicResultData);
            }
            else
            {
                if (_log.IsDebugEnabled)
                {
                    _log.Debug(string.Format(Properties.Resources.D_NOT_CONVERT_DATA_SET, "Reverse"));
                }
            }
        }

        /// <summary>
        /// ビジネスロジックを非同期処理で実行します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// ビジネスロジックを非同期処理で実行します。
        /// </para>
        /// <para>
        /// 画面 <see cref="DataSet"/> に対して入力値検証を行います。
        /// 入力値検証は、入力値検証機能による単項目チェックと、
        /// <see cref="EventController"/> のカスタムチェックイベントによる検証で行うことできます。
        /// 入力値検証失敗時には同期処理は実行されず、
        /// <see cref="ExecutionResult"/> の結果文字列に
        /// <see cref="Coordinator.VALIDATION_FAILED"/> が格納され、
        /// <see cref="FormBase"/> のエラーハンドラ及び、
        /// <see cref="EventController"/> の入力値検証失敗イベントを実行し、
        /// 非同期処理を終了します。
        /// </para>
        /// <para>
        /// 画面 <see cref="DataSet"/> の内容を、 <see cref="EventController"/> に設定されている
        /// ビジネスロジック入力 <see cref="DataSet"/> に詰め替えます。
        /// </para>
        /// <para>
        /// ビジネスロジック入力 <see cref="DataSet"/> に対して前処理を行います。
        /// 前処理は、 <see cref="EventController"/> の前処理イベントで行うことができます。
        /// 前処理失敗時には同期処理は実行されず、 
        /// <see cref="ExecutionResult"/> の結果文字列に 
        /// <see cref="Coordinator.PREPROCESSED_FAILED"/> 、または前処理イベントで設定した任意の結果文字列が格納され、
        /// <see cref="FormBase"/> のエラーハンドラ及び、
        /// <see cref="EventController"/> の前処理失敗イベントを実行し、
        /// 非同期処理を終了します。
        /// </para>
        /// <para>
        /// ビジネスロジックを非同期実行します。
        /// <see cref="ExecutionResult.ResultString"/> が <see cref="BLogicResult.SUCCESS"/> の場合、
        /// ビジネスロジックの結果 <see cref="DataSet"/> を画面 <see cref="DataSet"/> に変換を行い、
        /// <see cref="EventController"/> の非同期処理完了イベントを実行します。
        /// <see cref="ExecutionResult.ResultString"/> が <see cref="BLogicResult.SUCCESS"/> 以外の場合、
        /// <see cref="FormBase"/> のエラーハンドラ及び、
        /// <see cref="EventController"/> のイベント処理失敗イベントを実行します。
        /// </para>
        /// </remarks>
        /// <param name="eventController">
        /// <see cref="ExecuteAsync"/> メソッドの呼び出し元である 
        /// <see cref="EventController"/> のインスタンス。
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// この例外は <see cref="ExecuteCompletedEventArgs"/> に格納されます。
        /// <para>
        /// <paramref name="eventController"/> が null 参照です。
        /// </para>
        /// </exception>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外が発生し、 <see cref="ExecuteCompletedEventArgs"/> に格納されます。
        /// <list type="bullet">      
        /// <item>
        /// <description>
        /// <see cref="Coordinator.EventController"/> の ValidationFilePath を設定したが、
        /// <see cref="Coordinator.EventController"/> の View が null 参照の場合。
        /// </description>
        /// </item>        
        /// <item>
        /// <description>
        /// <see cref="Coordinator.EventController"/> の ConvertId を設定したが、
        /// <see cref="Coordinator.EventController"/> の View が null 参照の場合。
        /// </description>
        /// </item>    
        /// <item>
        /// <description>
        /// <see cref="Coordinator.EventController"/> の ConvertId を設定したが、
        /// <see cref="Coordinator.BLogic"/> の <see cref="IBLogic.Execute"/> 
        /// 実行結果である <see cref="BLogicResult"/> の ResultData が null 参照の場合。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// <see cref="IValidator"/> 実装クラスのインスタンスを生成できません。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// <see cref="IConverter"/> 実装クラスのインスタンスを生成できません。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// ビジネスロジック入力 <see cref="DataSet"/> のインスタンスを生成できません。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// <see cref="IBLogic"/> 実装クラスのインスタンスを生成できません。
        /// </description>
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="ConversionException">
        /// この例外は <see cref="ExecuteCompletedEventArgs"/> に格納されます。
        /// <para>
        /// データセット変換中にエラーが発生しました。
        /// </para>
        /// </exception>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public virtual void ExecuteAsync(EventController eventController)
        {
            if (eventController == null)
            {
                ArgumentNullException exception = new ArgumentNullException("eventController");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(
                        Properties.Resources.E_NULL_ARGUMENT, "eventController"), exception);
                }
                throw exception;
            }

            // 他に非同期で実行中ではないかをチェック
            if (_isBusy)
            {
                InvalidOperationException exception = new InvalidOperationException(
                    Properties.Resources.E_BLOGIC_EXECUTEASYNC_OPERATED);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            // 非同期処理を実行中に設定
            _isBusy = true;

            _eventController = eventController;

            ExecutionResult executionResult = new ExecutionResult();

            try
            {
                // エラークリア
                ClearError(eventController.ErrorHandler, eventController.ViewData, eventController.ErrorPaths);

                // Validator, Converterの初期化
                Initialize();

                // 前処理を行い、ビジネスロジック入力データセットを取得する。
                BLogicParam blogicParam = Preprocess(eventController, executionResult);
                if (!string.IsNullOrEmpty(executionResult.ResultString))
                {
                    eventController.ReportExecuteCompleted(executionResult, null, false);
                    _isBusy = false;
                    return;
                }

                // BLogic実行(非同期)
                _blogic = CreateBLogic(
                    eventController.BLogicName, eventController.BLogicResultTypeName, eventController.ViewData);
                _asyncOpe = AsyncOperationManager.CreateOperation(null);


                if (_log.IsDebugEnabled)
                {
                    _log.Debug(Properties.Resources.D_DO_EXECUTE_ASYNC);
                }

                _doExecuteAsyncDelegate.BeginInvoke(blogicParam, executionResult, null, null);
            }
            catch (Exception)
            {
                _isBusy = false;
                throw;
            }
        }

        /// <summary>
        /// ビジネスロジックを別スレッドで実行し、非同期処理を実現します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// <see cref="ExecuteAsync"/> メソッドから呼び出されます。
        /// ビジネスロジックを別スレッドで実行し、非同期処理を実現します。
        /// 実行終了後は <see cref="ExecuteAsyncCompletedCallback"/> を呼び出します。
        /// </para>
        /// <para>
        /// ビジネスロジックで例外が発生した場合は、すべての例外が <see cref="ExecutionResult"/> に格納され、
        /// <see cref="ExecuteAsyncCompletedCallback"/> を呼び出して終了します。
        /// </para>
        /// </remarks>
        /// <param name="blogicParam">ビジネスロジック入力 <see cref="BLogicParam"/>。</param>
        /// <param name="executionResult">実行結果を格納する <see cref="ExecutionResult"/>。</param>
        /// <exception cref="TerasolunaException">
        /// ビジネスロジックからの戻り値 <see cref="BLogicResult"/> が null 参照です。
        /// </exception>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        protected virtual void DoExecuteAsync(BLogicParam blogicParam, ExecutionResult executionResult)
        {
            BLogicResult blogicResult = null;
            Exception error = null;

            // ビジネスロジックを実行
            if (_log.IsDebugEnabled)
            {
                _log.Debug(string.Format(Properties.Resources.D_BEGIN_WORKER_THREAD, Thread.CurrentThread.ManagedThreadId));
            }

            try
            {
                blogicResult = ExecuteBLogic(blogicParam);
            }
            catch (Exception e)
            {
                if (_log.IsWarnEnabled)
                {
                    _log.Warn(Properties.Resources.W_BLOGIC_EXECUTEASYNC_EXCEPTION, e);
                }
                error = e;
            }

            // ビジネスロジックの結果がnullかつ、ビジネスロジックで例外が発生していない場合
            if (blogicResult == null && error == null)
            {
                // ビジネスロジックで例外が発生していない場合のみ、TerasolunaExceptionをerrorにつめる
                error = new TerasolunaException(Properties.Resources.E_BLOGIC_NULL_RESULT);
                if (_log.IsWarnEnabled)
                {
                    _log.Warn(Properties.Resources.W_BLOGIC_NULL_RESULT, error);
                }
            }

            bool cancelled = false;
            if (_blogic is ICancelable)
            {
                cancelled = ((ICancelable)_blogic).Cancelled;
            }

            if (_log.IsDebugEnabled)
            {
                _log.Debug(string.Format(Properties.Resources.D_END_WORKER_THREAD, Thread.CurrentThread.ManagedThreadId));
            }

            // コールバックメソッドの呼び出し
            BLogicCompletedEventArgs eceArgs =
                new BLogicCompletedEventArgs(executionResult, blogicResult, error, cancelled);
            _asyncOpe.PostOperationCompleted(_executeAsyncCompletedCallback, eceArgs);
        }

        /// <summary>
        /// 非同期処理の実行完了後に呼び出されるコールバックメソッドです。
        /// </summary>
        /// <remarks>
        /// <para>
        /// 非同期処理の実行完了後に呼び出されるコールバックメソッドです。
        /// ビジネスロジック出力 <see cref="DataSet"/> から
        /// 画面 <see cref="DataSet"/> への変換を行い、完了イベントを発生させます。
        /// </para>
        /// <para>
        /// ビジネスロジックの実行がキャンセルされた場合は、
        /// <see cref="AsyncCompletedEventArgs.Cancelled"/> を true にして
        /// 完了イベントを発生させます。
        /// </para>
        /// <para>
        /// ビジネスロジックで例外が発生した場合は、<see cref="ExecuteCompletedEventArgs"/> に発生した例外を 
        /// 格納して完了イベントを発生させます。
        /// </para>
        /// <para>
        /// ビジネスロジック出力 <see cref="DataSet"/> から
        /// 画面 <see cref="DataSet"/> への変換で例外が発生した場合は、
        /// その例外を <see cref="ExecuteCompletedEventArgs"/> に格納して完了イベントを発生させます。
        /// </para>
        /// </remarks>
        /// <param name="obj">
        /// <see cref="DoExecuteAsync"/> メソッドから渡される <see cref="ExecuteCompletedEventArgs"/>。
        /// </param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        protected virtual void ExecuteAsyncCompletedCallback(object obj)
        {
            if (_log.IsDebugEnabled)
            {
                _log.Debug(Properties.Resources.D_ASYNC_COMPLETED_CALLBACK);
            }

            ExecutionResult executionResult = null;
            BLogicCompletedEventArgs blogicArgs = (BLogicCompletedEventArgs)obj;
            ExecuteCompletedEventArgs executeArgs = new ExecuteCompletedEventArgs(
                blogicArgs.ExecutionResult, blogicArgs.Error, blogicArgs.Cancelled);
            Exception error = executeArgs.Error;
            
            try
            {
                // ビジネスロジックでの処理が正常に終了したとき
                if (!executeArgs.Cancelled && error == null)
                {
                    executionResult = executeArgs.ExecutionResult;
                    Postprocess(_eventController, executionResult, blogicArgs.BLogicResult);
                }
                _eventController.ReportExecuteCompleted(executionResult, error, executeArgs.Cancelled);
            }
            finally
            {
                _isBusy = false;
            }
        }

        /// <summary>
        /// 非同期処理を実行中のビジネスロジックにキャンセルを要求します。
        /// </summary>
        /// <remarks>
        /// 非同期処理を実行中のビジネスロジックの <see cref="ICancelable.Cancel"/> メソッドを実行します。
        /// <para>
        /// ビジネスロジックが非同期で実行中でない場合や <see cref="ICancelable"/> を実装していない
        /// 場合は、<see cref="InvalidOperationException"/> を発生します。
        /// </para>
        /// </remarks>
        /// <exception cref="InvalidOperationException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">      
        /// <item>
        /// ビジネスロジックが非同期で実行されていません。
        /// </item>
        /// <item>
        /// ビジネスロジックが <see cref="ICancelable"/> を実装していません。
        /// </item>
        /// </list>
        /// </exception>
        public virtual void ExecuteAsyncCancel()
        {
            // 非同期処理が実行されていない場合
            if (!_isBusy || _blogic == null)
            {
                InvalidOperationException exception = new InvalidOperationException(
                    Properties.Resources.E_BLOGIC_NOT_EXECUTEASYNC_OPERATED);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            // ビジネスロジックがICancelableを実装していない場合は例外
            if (!(_blogic is ICancelable))
            {
                InvalidOperationException exception = new InvalidOperationException(
                    Properties.Resources.E_BLOGIC_NOT_IMPLEMENTS_ICANCELABLE);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            ((ICancelable)_blogic).Cancel();
        }

        /// <summary>
        /// フォームのエラーハンドラを実行します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// <see cref="Coordinator.EventController"/> が <see cref="TERASOLUNA.Fw.Client.EventController.ErrorHandler"/> を保持しているとき、
        /// エラー発生時に呼び出されます。
        /// </para>
        /// <para>
        /// エラー発生箇所を表すキーを <see cref="Coordinator.EventController"/> の ErrorPaths へ格納します。
        /// </para>
        /// </remarks>
        /// <param name="errorHandler">エラー処理を委譲するエラー処理ハンドラ。</param>
        /// <param name="executionResult">実行結果の格納された <see cref="ExecutionResult"/>。</param>
        /// <param name="viewData">画面 <see cref="DataSet"/>。</param>
        /// <param name="errorPaths">エラー発生箇所を表すキーのリスト。</param>
        protected virtual void HandleError(
            IErrorHandler errorHandler, ExecutionResult executionResult, DataSet viewData, IList<string> errorPaths)
        {
            if (errorHandler != null)
            {
                errorHandler.HandleError(executionResult.ResultString, executionResult.Errors, viewData);
            }

            foreach (MessageInfo error in executionResult.Errors)
            {
                ValidationMessageInfo validationMessage = error as ValidationMessageInfo;
                if (validationMessage != null)
                {
                    errorPaths.Add(validationMessage.ErrorPath);
                }
            }
        }

        /// <summary>
        /// フォームのエラークリア処理を実行します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// <see cref="Coordinator.EventController"/> が <see cref="Client.EventController.ErrorHandler"/> を保持しているとき、
        /// <see cref="Client.EventController.ErrorHandler"/> の <see cref="IErrorHandler.ClearError"/> を実行します。
        /// </para>
        /// </remarks>
        /// <param name="errorHandler">エラー処理を委譲するエラー処理ハンドラ。</param>
        /// <param name="viewData">画面 <see cref="DataSet"/>。</param>
        /// <param name="errorPaths">エラー発生箇所を表すキーのリスト。</param>
        protected virtual void ClearError(IErrorHandler errorHandler, DataSet viewData, IList<string> errorPaths)
        {
            if (errorHandler != null)
            {
                errorHandler.ClearError(viewData);
            }
            errorPaths.Clear();
        }

        /// <summary>
        /// <see cref="IBLogic"/> が発行する <see  cref="IProgressChangedEventInvoker.ProgressChanged"/> を
        /// <see cref="Client.EventController.ReportExecuteProgressChanged"/> に委譲します。
        /// </summary>
        /// <param name="sender">イベントのソース。</param>
        /// <param name="e">イベント データを格納している <see cref="ExecuteProgressChangedEventArgs"/>。</param>
        /// <exception cref="ArgumentNullException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <description>
        /// <paramref name="sender"/> が null 参照です。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// <paramref name="e"/> が null 参照です。
        /// </description>
        /// </item>
        /// </list>
        /// </exception>
        protected virtual void DoExecuteProgressChanged(object sender, ExecuteProgressChangedEventArgs e)
        {
            if (sender == null)
            {
                ArgumentNullException exception = new ArgumentNullException("sender");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(
                        Properties.Resources.E_NULL_ARGUMENT, "sender"), exception);
                }
                throw exception;
            }

            if (e == null)
            {
                ArgumentNullException exception = new ArgumentNullException("e");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(
                        Properties.Resources.E_NULL_ARGUMENT, "e"), exception);
                }
                throw exception;
            }

            if (_asyncOpe == null)
            {
                // 同期実行
                _eventController.ReportExecuteProgressChanged(e.ProgressPercentage, e.Items);
            }
            else
            {
                // 非同期実行
                _asyncOpe.SynchronizationContext.Send(_executeAsyncProgressChangedCallback, e);
            }
        }

        /// <summary>
        /// 進行状況イベントを発生させます。
        /// </summary>
        /// <param name="obj">進行状況イベントの <see cref="ExecuteProgressChangedEventArgs"/>。</param>
        protected virtual void ExecuteAsyncProgressChangedCallback(object obj)
        {
            ExecuteProgressChangedEventArgs e = (ExecuteProgressChangedEventArgs)obj;
            _eventController.ReportExecuteProgressChanged(e.ProgressPercentage, e.Items);
        }

    }

    /// <summary>
    /// ビジネスロジックを別スレッドで実行し、非同期処理を実現するデリゲートです。
    /// </summary>
    /// <param name="blogicParam">ビジネスロジックへの入力となる <see cref="BLogicParam"/>。</param>
    /// <param name="executionResult">実行結果を格納する <see cref="ExecutionResult"/>。</param>
    public delegate void DoExecuteAsyncDelegate(BLogicParam blogicParam, ExecutionResult executionResult);

}
