// 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 TERASOLUNA.Fw.Common.Logging;

namespace TERASOLUNA.Fw.Common.Validation
{
    /// <summary>
    /// TERASOLUNA が提供する、 <see cref="IValidator"/> 実装クラスのインスタンスを生成するファクトリクラスです。
    /// </summary>
    public class ValidatorFactory
    {
        /// <summary>
        /// <see cref="ILog"/> 実装クラスのインスタンスです。
        /// </summary>
        /// <remarks>
        /// ログ出力に利用します。
        /// </remarks>
        private static ILog _log = LogFactory.GetLogger(typeof(ValidatorFactory));

        /// <summary>
        /// 利用する <see cref="IValidator"/> 実装クラスの型名をアプリケーション構成
        /// ファイルから取得する際に指定する文字列です。
        /// </summary>
        /// <remarks>
        /// <para>
        /// この定数の値は "ValidatorTypeName" です。
        /// </para>
        /// </remarks>
        protected static readonly string VALIDATOR_TYPENAME = "ValidatorTypeName";

        /// <summary>
        /// 利用する <see cref="ValidatorFactory"/> クラスの型名をアプリケーション構成
        /// ファイルから取得する際に指定する文字列です。
        /// </summary>
        /// <remarks>
        /// <para>
        /// この定数の値は "ValidatorFactoryTypeName" です。
        /// </para>
        /// </remarks>
        protected static readonly string VALIDATOR_FACTORY_TYPENAME = "ValidatorFactoryTypeName";

        /// <summary>
        /// <see cref="ValidatorFactory"/> のシングルトンインスタンスです。
        /// </summary>
        private static volatile ValidatorFactory _factory = null;

        /// <summary>
        /// ロック用のオブジェクトです。
        /// </summary>
        private static object _syncRoot = new Object();

        /// <summary>
        /// <see cref="IValidator"/> 実装クラスの型のキャッシュです。
        /// </summary>
        private Type _validatorType = null;

        /// <summary>
        /// <see cref="IValidator"/> 実装クラスの型のキャッシュを取得または設定します。
        /// </summary>
        /// <value>
        /// <see cref="IValidator"/> 実装クラスの型。
        /// </value>
        /// <remarks>
        /// デフォルトの値は、 null です。
        /// </remarks>
        protected Type ValidatorType
        {
            get
            {
                return _validatorType;
            }

            set
            {
                _validatorType = value;
            }
        }

        /// <summary>
        /// <see cref="ValidatorFactory"/> 型のクラスのインスタンスを取得します。
        /// </summary>
        /// <remarks>
        /// App.config に <seealso cref="VALIDATOR_FACTORY_TYPENAME"/> をキーとして型名が
        /// 記述されていた場合、その型のインスタンスを生成します。記述がない場合、
        /// <see cref="ValidatorFactory"/> のインスタンスを生成します。
        /// </remarks>
        /// <value><see cref="ValidatorFactory"/> 型のクラスのインスタンス。</value>
        /// <exception cref="TerasolunaException">
        /// <see cref="ValidatorFactory"/> 派生クラスのインスタンスを生成できません。
        /// </exception>
        protected static ValidatorFactory Factory
        {
            get
            {
                if (_factory == null)
                {
                    lock (_syncRoot)
                    {
                        if (_factory == null)
                        {
                            _factory = ClassUtil.CreateInstanceFromAppSettings<ValidatorFactory>(
                              VALIDATOR_FACTORY_TYPENAME, typeof(ValidatorFactory));
                        }
                    }
                }
                return _factory;
            }
        }

        /// <summary>
        /// <see cref="ValidatorFactory"/> クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <remarks>
        /// デフォルトコンストラクタです。 
        /// </remarks>
        public ValidatorFactory()
        {
        }

        /// <summary>
        /// <see cref="IValidator"/> 実装クラスのインスタンスを生成します。
        /// </summary>
        /// <remarks>
        /// <see cref="ValidatorFactory"/> 派生クラスのクラスのインスタンスを生成し、
        /// そのインスタンスの <seealso cref="CreateInstance()"/> メソッドを呼び出して
        /// <see cref="IValidator"/> 実装クラスのインスタンスを生成します。
        /// App.config に <seealso cref="VALIDATOR_FACTORY_TYPENAME"/> をキーとして
        /// <see cref="ValidatorFactory"/> 派生クラスの型名が
        /// 記述されていた場合、その型のインスタンスを生成して利用します。記述がない場合、
        /// <see cref="ValidatorFactory"/> のインスタンスを生成して利用します。
        /// <see cref="ValidatorFactory"/> を利用する場合、
        /// App.config に <seealso cref="VALIDATOR_TYPENAME"/> をキーとして型名が
        /// 記述されていれば、その型のインスタンスを生成します。記述がない場合、
        /// <see cref="VabValidator"/> のインスタンスを生成します。
        /// </remarks>
        /// <returns><see cref="IValidator"/> 実装クラスのインスタンス。</returns>
        /// <example>
        /// <code>
        /// //引数なしのパブリックなコンストラクタを持つ IValidator の実装クラス
        /// namespace TERASOLUNA.Sample {
        ///   public class SampleValidator : IValidator
        ///   {
        ///     public SampleValidator()
        ///     {
        ///     }
        /// 
        ///     public override ValidationResult ValidateValue(object value, string rule);
        ///       return null;
        ///     }
        ///   }
        /// }
        /// </code>
        /// <code>
        /// &lt;!-- 設定ファイルよりデフォルトの型名を取得 --&gt;
        /// &lt;appSettings&gt;
        ///   &lt;add key="ValidatorTypeName" value="TERASOLUNA.Sample.SampleValidator" /&gt;
        /// &lt;/appSettings&gt;
        /// </code>
        /// <code>
        /// IValidator validator = ValidatorFactory.CreateInstance();
        /// </code>
        /// </example>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <see cref="ValidatorFactory"/> 派生クラスのインスタンスを生成できません。
        /// </item>
        /// <item>
        /// <see cref="IValidator"/> 実装クラスのインスタンスを生成できません。
        /// </item>
        /// </list>
        /// </exception>
        public static IValidator CreateValidator()
        {
            IValidator validator = Factory.CreateInstance();

            if (_log.IsDebugEnabled)
            {
                _log.Debug(string.Format(Properties.Resources.D_CREATE_INSTANCE_SUCCESS,
                    typeof(IValidator).FullName, validator.GetType().FullName));
            }

            return validator;
        }

        /// <summary>
        /// <see cref="IValidator"/> 実装クラスのインスタンスを生成します。
        /// </summary>
        /// <returns>
        /// <see cref="IValidator"/> 実装クラスのインスタンス。
        /// </returns>
        /// <remarks>
        /// <para>
        /// アプリケーション構成ファイルより、<seealso cref="VALIDATOR_TYPENAME"/>
        /// をキーとして実装クラスの型を取得します。
        /// </para>
        /// <para>
        /// アプリケーション構成ファイルに <seealso cref="VALIDATOR_TYPENAME"/> のキーが
        /// 存在しない場合、<seealso cref="VabValidator"/> のインスタンスを生成します。
        /// </para>
        /// <para>
        /// このメソッドはクラスメソッドの <see cref="ValidatorFactory.CreateValidator"/> から
        /// 呼び出されます。業務プログラマが直接呼び出すことは通常ありません。
        /// </para>
        /// <para>
        /// 生成する <see cref="IValidator"/> 実装クラスには引数なしのパブリックなコンストラクタが
        /// 定義されている必要があります。定義されていない場合、例外をスローします。
        /// </para>
        /// </remarks>
        /// <exception cref="TerasolunaException">
        /// <see cref="IValidator"/> 実装クラスのインスタンスを生成できません。
        /// </exception>
        protected virtual IValidator CreateInstance()
        {
            IValidator validator = null;

            if (_validatorType == null)
            {
                // 型がキャッシュされていなければ設定ファイルから型名を取得して生成
                validator =
                    ClassUtil.CreateInstanceFromAppSettings<IValidator>(VALIDATOR_TYPENAME, typeof(VabValidator));

                // 生成したインスタンスの型をキャッシュ
                _validatorType = validator.GetType();
            }
            else
            {
                // 型がキャッシュされていれば、その型のインスタンスを生成
                validator =
                    ClassUtil.CreateInstanceByType<IValidator>(_validatorType);
            }

            return validator;
        }
    }
}
