// 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.Text.RegularExpressions;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
using TERASOLUNA.Fw.Common.Logging;
using TERASOLUNA.Fw.Common.Validation.Configuration;


namespace TERASOLUNA.Fw.Common.Validation.Validators
{
    /// <summary>
    /// 検証ルール"正規表現チェック"に対応するパターン一致チェックを行う <see cref="Microsoft.Practices.EnterpriseLibrary.Validation.Validator"/> 継承クラスです。
    /// </summary>
    /// <remarks>
    /// <para>
    /// 検証対象の値が文字列表現である時、指定された正規表現パターンにマッチするかチェックする機能を提供します。
    ///  null または空文字列に対しては検証は行わないため、 <seealso cref="ValueValidator.Negated"/> の状態に関わらず、必ず検証成功となります。 
    /// </para>
    /// <para>
    /// 本クラスを用いて入力値検証を実施するには、入力値検証設定ファイルに記述する
    ///  validator 要素の属性として、以下の設定を行います。
    /// 入力値検証設定ファイル自体の記法については、
    ///  Validation Application Block のドキュメントを参照してください。 
    /// </para>
    /// <para>
    /// <list type="bullet">
    /// <item>
    /// <description>negated</description>
    /// <para>検証ルールを反転するフラグです。 true を設定した場合、検証ルールが反転します。デフォルト値は、 false です。</para>
    /// </item>
    /// <item>
    /// <description>pattern</description>
    /// <para>チェックを行う正規表現パターン文字列を指定します。</para>
    /// </item>
    /// <item>
    /// <description>options</description>
    /// <para>正規表現オプション列挙体の値。<code>None, IgnoreCase, Multiline, ExplicitCapture, Compiled, Singleline, IgnorePatternWhiteSpace, RighttoLeft</code>の値の一つまたは複数の組み合わせを記述します。複数の値を指定する場合には、<code>，</code>で区切って指定してください。
    /// </para>
    /// </item>
    /// <item>
    /// <description>patternResourceName</description>
    /// <para>正規表現パターン文字列をリソースから取得する際に利用するキー文字列を指定します。</para>
    /// </item>
    /// <item>
    /// <description>patternResourceType</description>
    /// <para>正規表現パターン文字列をリソースから取得する際に利用するリソース型をアセンブリ修飾名で指定します。</para>
    /// </item>   
    /// <item>
    /// <description>messageTemplate</description>
    /// <para>検証エラーメッセージのテンプレートとして使用する文字列。
    /// 未設定の場合は、 <seealso cref="NumericStringValidator.DefaultNonNegatedMessageTemplate"/> 、 <seealso cref="NumericStringValidator.DefaultNegatedMessageTemplate"/> の 
    /// いずれかが使用されます。
    /// </para>
    /// </item>
    /// <item>
    /// <description>messageTemplateResourceName</description>
    /// <para>メッセージテンプレートをリソースから読み込む際の名前。</para>
    /// </item>
    /// <item>
    /// <description>messageTemplateResourceType</description>
    /// <para>メッセージテンプレートをリソースから読み込む際のリソースの型。</para>
    /// </item>
    /// <item>
    /// <description>tag</description>
    /// <para>メッセージテンプレートのプレースホルダ{2}に渡される文字列。
    /// 検証対象項目の論理名を指定します。</para>
    /// </item>
    /// <item>
    /// <description>type</description>
    /// <para>使用する <see cref="Microsoft.Practices.EnterpriseLibrary.Validation.Validator"/> クラスの型を完全修飾型名で指定します。</para>
    /// </item>
    /// <item>
    /// <description>name</description>
    /// <para>使用する <see cref="Microsoft.Practices.EnterpriseLibrary.Validation.Validator"/> に名前をつけます。特に利用しない値です。 
    /// 任意の名前をつけることができますが、同一の検証対象の場合、同一の名前をつけることは許されません。
    /// また、空文字列も許されません。必須です。</para>
    /// </item>
    /// </list>
    /// </para>
    /// <example>
    /// <code>
    /// &lt;validator negated="false"  pattern="[0-9a-zA-Z]+.@sample.co.jp" options="None" patternResourceName=""
    /// patternResourceType="" messageTemplate="" messageTemplateResourceName=""
    /// messageTemplateResourceType="" tag="検証対象項目名" name="numeric validator"  
    /// type="TERASOLUNA.Fw.Common.Validation.Validators.RegexStringValidator,TERASOLUNA.Fw.Common"/&gt;
    /// </code>
    /// </example>
    /// </remarks>
    [ConfigurationElementType(typeof(RegexValidatorExData))]
    public class RegexValidatorEx : RegexValidator
    {
        /// <summary>
        /// <see cref="ILog"/> 実装クラスのインスタンスです。
        /// </summary>
        /// <remarks>
        /// ログ出力に利用します。
        /// </remarks>
        private static ILog _log = LogFactory.GetLogger(typeof(RegexValidatorEx));

        /// <summary>
        /// 新しい <see cref="RegexValidatorEx"/> のインスタンスを初期化します。
        /// </summary>
        /// <param name="pattern">正規表現パターン。</param>
        /// <param name="patternResourceName">正規表現パターンをリソースから取得する際に利用するキー文字列。</param>
        /// <param name="patternResourceType">正規表現パターンをリソースから取得する際に利用するリソース型。</param>
        /// <param name="options">正規表現オプション。</param>
        /// <param name="messageTemplate">検証失敗の際、メッセージを生成するために利用するテンプレート文字列。
        /// 検証に利用するパラメータがプレースホルダに渡されます。
        /// パラメータとそのプレースホルダ番号は以下の通りです。
        /// <list type="table">
        /// <listheader>
        /// <term>プレースホルダ</term>
        /// <description>内容</description>
        /// </listheader>
        /// <item>
        /// <term>{0}</term>
        /// <description>検証対象文字列</description>
        /// </item>
        /// <item>
        /// <term>{1}</term>
        /// <description>key(プロパティ・メソッド名)</description>
        /// </item>
        /// <item>
        /// <term>{2}</term>
        /// <description>Tag文字列</description>
        /// </item>
        /// <item>
        /// <term>{3}</term>
        /// <description>正規表現パターン</description>
        /// </item>
        /// <item>
        /// <term>{4}</term>
        /// <description>正規表現オプション</description>
        /// </item>
        /// </list>
        /// </param>
        /// <param name="negated">検証条件の反転を示すフラグ。</param>
        public RegexValidatorEx(string pattern, string patternResourceName, Type patternResourceType, RegexOptions options, string messageTemplate, bool negated)
            : base(pattern, patternResourceName, patternResourceType, options, messageTemplate, negated)
        {
        }

        /// <summary>
        /// <seealso cref="ValueValidator.Negated"/> が <code>true</code> である場合に使用されるデフォルトのメッセージテンプレートを取得します。
        /// </summary>
        /// <value>検証ルール反転時のデフォルトメッセージテンプレート文字列。</value>
        protected override string DefaultNegatedMessageTemplate
        {
            get
            {
                return Properties.Resources.REGEX_VALIDATOR_MESSAGETEMPLATE;
            }
        }

        /// <summary>
        /// <seealso cref="ValueValidator.Negated"/> が <code>false</code> である場合に使用されるデフォルトのメッセージテンプレートを取得します。
        /// </summary>
        /// <value>デフォルトメッセージテンプレート文字列。</value>
        protected override string DefaultNonNegatedMessageTemplate
        {
            get
            {
                return Properties.Resources.REGEX_VALIDATOR_MESSAGETEMPLATE;
            }
        }

        /// <summary>
        /// 正規表現チェックを行うメソッドです。親クラスの処理をラップし、検証対象値が null または空文字だった場合には
        /// 検証成功とします。
        /// </summary>
        /// <param name="objectToValidate">検証対象の値。</param>
        /// <param name="currentTarget">検証対象のオブジェクト。</param>
        /// <param name="key">検証対象のプロパティ名またはメソッド名。</param>
        /// <param name="validationResults">検証結果を格納する <see cref="ValidationResults"/> 。</param>
        protected override void DoValidate(string objectToValidate, object currentTarget, string key, ValidationResults validationResults)
        {
            if (validationResults == null)
            {
                ArgumentNullException exception = new ArgumentNullException("validationResults");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(
                        Properties.Resources.E_NULL_ARGUMENT, "validationResults"), exception);
                }
                throw exception;
            }

            if (string.IsNullOrEmpty(objectToValidate))
            {
                return;
            }

            base.DoValidate(objectToValidate, currentTarget, key, validationResults);
        }
    }
}
