// 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.Configuration;
using System.Reflection;
using TERASOLUNA.Fw.Common.Logging;

namespace TERASOLUNA.Fw.Common
{
    /// <summary>
    /// インスタンスを生成するためのユーティリティクラスです。
    /// </summary>
    /// <remarks>
    /// <para>
    /// <see cref="ClassUtil"/> は通常、フレームワーク内部で利用されます。フレームワークの利用者が
    /// 直接呼び出すことはありません。
    /// </para>
    /// <para>
    /// リフレクションを利用して、インスタンスを生成します。
    /// </para>
    /// </remarks>
    public static class ClassUtil
    {
        /// <summary>
        /// <see cref="ILog"/> 実装クラスのインスタンスです。
        /// </summary>
        /// <remarks>
        /// ログ出力に利用します。
        /// </remarks>
        private static ILog _log = LogFactory.GetLogger(typeof(ClassUtil));

        /// <summary>
        /// 構成ファイルから、キーを指定して型名を取得し、リフレクションを用いて
        /// インスタンスを生成します。型名が取得できない場合は例外をスローします。
        /// </summary>
        /// <typeparam name="T"> 生成したインスタンスを受ける型。</typeparam>
        /// <param name="appSettingsKey"> 構成ファイルから型名を取得するために用いるキー
        /// 文字列。</param>
        /// <returns>生成された <typeparamref name="T"/> 型のインスタンス。</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="appSettingsKey"/> が null 参照です。
        /// </exception>
        /// <exception cref="ArgumentException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="appSettingsKey"/> が空文字列です。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="ConfigurationErrorsException">
        /// 構成ファイルが不正です。
        /// </exception>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素が未定義、または <paramref name="appSettingsKey"/>を
        /// キーとする構成ファイルのappSettings要素のvalue属性が空文字です。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値から
        /// 型を解決することができません。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値で
        /// 指定された型は抽象クラスなので、生成できません。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値のアセンブリ修飾名はジェネリック型クラスです。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値から
        /// 生成した型のデフォルトコンストラクタを呼び出すことができません。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値から
        /// 生成した型のデフォルトコンストラクタが例外をスローしました。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値から
        /// 生成した型のインスタンスが <typeparamref name="T"/> にキャストすることができません。
        /// </item>
        /// </list>
        /// </exception>
        public static T CreateInstanceFromAppSettings<T>(string appSettingsKey) where T : class
        {
            // 入力チェック
            if (appSettingsKey == null)
            {
                ArgumentNullException exception = new ArgumentNullException("appSettingsKey");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(Properties.Resources.E_NULL_ARGUMENT, "appSettingsKey"), exception);
                }
                throw exception;
            }
            if (appSettingsKey.Length == 0)
            {
                string message = string.Format(Properties.Resources.E_EMPTY_STRING, "appSettingsKey");
                ArgumentException exception = new ArgumentException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }

            //　インスタンスの生成
            T result = DoCreateInstanceFromAppSettings<T>(appSettingsKey, null);

            return result;
        }

        /// <summary>
        /// 構成ファイルから、キーを指定して型名を取得し、リフレクションを用いて
        /// インスタンスを生成します。以下の場合、<paramref name="defaultType"/> で指定
        /// された <see cref="Type"/> のインスタンスを生成します。
        /// <list type="bullet">
        /// <item>
        /// 構成ファイルの AppSettings 要素に <paramref name="appSettingsKey"/> をキーとする add 要素が未定義。
        /// </item>
        /// <item>
        /// 構成ファイルの AppSettings 要素に <paramref name="appSettingsKey"/> をキーとする add 要素が定義されているが、value 属性が空文字。
        /// </item>
        /// </list>
        /// </summary>
        /// <typeparam name="T"> 生成したインスタンスを受ける型。</typeparam>
        /// <param name="appSettingsKey"> 構成ファイルから型名を取得するために用いるキー文字列。</param>
        /// <param name="defaultType"> 構成ファイルから型名が取得できなかった場合、
        /// 利用される型。</param>
        /// <returns>生成された <typeparamref name="T"/> 型のインスタンス。</returns>
        /// <exception cref="ArgumentNullException">
        /// <list type="bullet">
        /// <item>
        /// <paramref name="appSettingsKey"/> が null 参照です。
        /// </item>
        /// <item>
        /// <paramref name="defaultType"/> が null 参照です。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="ArgumentException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="appSettingsKey"/> が空文字列です。
        /// </item>
        /// <item>
        /// <paramref name="defaultType"/> は抽象クラスです。
        /// </item>
        /// <item>
        /// <paramref name="defaultType"/> はジェネリック型のクラスですが、型パラメータが設定されていません。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="ConfigurationErrorsException">
        /// 構成ファイルが不正です。
        /// </exception>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="defaultType"/> のデフォルトコンストラクタを呼び出すことができません。
        /// </item>
        /// <item>
        /// <paramref name="defaultType"/> のデフォルトコンストラクタが例外をスローしました。
        /// </item>
        /// <item>
        /// <paramref name="defaultType"/> のインスタンスが <typeparamref name="T"/> にキャストすることができません。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値から
        /// 型を解決することができません。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値で
        /// 指定された型は抽象クラスなので、生成できません。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値のアセンブリ修飾名から生成した型が、ジェネリック型であるが型パラメータが未定義です。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値から
        /// 生成した型のデフォルトコンストラクタを呼び出すことができません。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値から
        /// 生成した型のデフォルトコンストラクタが例外をスローしました。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素から取得した
        /// 生成した型のインスタンスが <typeparamref name="T"/> にキャストすることができません。
        /// </item>
        /// </list>
        /// </exception>
        public static T CreateInstanceFromAppSettings<T>(
            string appSettingsKey, Type defaultType) where T : class
        {
            // 入力チェック
            if (appSettingsKey == null)
            {
                ArgumentNullException exception = new ArgumentNullException("appSettingsKey");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(Properties.Resources.E_NULL_ARGUMENT, "appSettingsKey"), exception);
                }
                throw exception;
            }
            if (appSettingsKey.Length == 0)
            {
                string message = string.Format(Properties.Resources.E_EMPTY_STRING, "appSettingsKey");
                ArgumentException exception = new ArgumentException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }
            if (defaultType == null)
            {
                ArgumentNullException exception = new ArgumentNullException("defaultType");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(Properties.Resources.E_NULL_ARGUMENT, "defaultType"), exception);
                }
                throw exception;
            }
            if (defaultType.IsAbstract)
            {
                string message = string.Format(Properties.Resources.E_PARAM_ABSTRACT_TYPE, "defaultType", defaultType.AssemblyQualifiedName);
                ArgumentException exception = new ArgumentException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }
            if (defaultType.ContainsGenericParameters)
            {
                string message = string.Format(Properties.Resources.E_PARAM_GENERIC_TYPE, "defaultType", defaultType.AssemblyQualifiedName);
                ArgumentException exception = new ArgumentException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }

            //　インスタンスの生成
            T result = DoCreateInstanceFromAppSettings<T>(appSettingsKey, defaultType);

            return result;
        }

        /// <summary>
        /// 構成ファイルから、キーを指定して型名を取得し、リフレクションを用いて
        /// インスタンスを生成します。以下の場合、<paramref name="defaultType"/> で指定
        /// された <see cref="Type"/> のインスタンスを生成します。
        /// <list type="bullet">
        /// <item>
        /// 構成ファイルの AppSettings 要素に <paramref name="appSettingsKey"/> をキーとする add 要素が未定義。
        /// </item>
        /// <item>
        /// 構成ファイルの AppSettings 要素に <paramref name="appSettingsKey"/> をキーとする add 要素が定義されているが、value 属性が空文字。
        /// </item>
        /// </list>
        /// </summary>
        /// <typeparam name="T"> 生成したインスタンスを受ける型。</typeparam>
        /// <param name="appSettingsKey"> 構成ファイルから型名を取得するために用いるキー文字列。</param>
        /// <param name="defaultType"> 構成ファイルから型名が取得できなかった場合、
        /// 利用される型。</param>
        /// <returns>生成された <typeparamref name="T"/> 型のインスタンス。</returns>
        /// <exception cref="ArgumentException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="defaultType"/> は抽象クラスです。
        /// </item>
        /// <item>
        /// <paramref name="defaultType"/> はジェネリック型のクラスですが、型パラメータが設定されていません。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="ConfigurationErrorsException">
        /// 構成ファイルが不正です。
        /// </exception>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="defaultType"/> のデフォルトコンストラクタを呼び出すことができません。
        /// </item>
        /// <item>
        /// <paramref name="defaultType"/> のデフォルトコンストラクタが例外をスローしました。
        /// </item>
        /// <item>
        /// <paramref name="defaultType"/> のインスタンスが <typeparamref name="T"/> にキャストすることができません。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素が未定義、または <paramref name="appSettingsKey"/>を
        /// キーとする構成ファイルのappSettings要素のvalue属性が空文字かつ、<paramref name="defaultType"/> が null 参照です。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値から
        /// 型を解決することができません。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値で
        /// 指定された型は抽象クラスなので、生成できません。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値のアセンブリ修飾名から生成した型が、ジェネリック型であるが型パラメータが未定義です。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値から
        /// 生成した型のデフォルトコンストラクタを呼び出すことができません。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素の、value属性値から
        /// 生成した型のデフォルトコンストラクタが例外をスローしました。
        /// </item>
        /// <item>
        /// <paramref name="appSettingsKey"/> をキーとする構成ファイルのappSettings要素から取得した
        /// 生成した型のインスタンスが <typeparamref name="T"/> にキャストすることができません。
        /// </item>
        /// </list>
        /// </exception>
        private static T DoCreateInstanceFromAppSettings<T>(
            string appSettingsKey, Type defaultType) where T : class
        {
            // 型名を構成ファイルから取得する。
            string typeName = ConfigurationManager.AppSettings[appSettingsKey];

            // 型名より型を取得する。
            // 型が取得できず、defaultTypeがnullの場合、例外とする
            T result = null;
            if (string.IsNullOrEmpty(typeName))
            {
                if (defaultType != null)
                {
                    if (_log.IsDebugEnabled)
                    {
                        _log.Debug(string.Format(
                            Properties.Resources.D_USE_DEFAULT_TYPE,
                            appSettingsKey, defaultType.AssemblyQualifiedName));
                    }

                    // インスタンスの生成
                    result = GetInstanceByType<T>(defaultType);
                }
                else
                {
                    string message = string.Format(Properties.Resources.E_TARGET_TYPE_NOT_FOUND, appSettingsKey);
                    TerasolunaException exception = new TerasolunaException(message);
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(message, exception);
                    }
                    throw exception;
                }
            }
            else
            {
                if (_log.IsDebugEnabled)
                {
                    _log.Debug(string.Format(
                        Properties.Resources.D_GET_TYPENAME_FROM_APPSETTINGS,
                        appSettingsKey, typeName));
                }

                // 型の取得
                Type type = CreateTypeByTypeName(typeName);

                // 取得した型の確認
                if (type.IsAbstract)
                {
                    string message = string.Format(Properties.Resources.E_ABSTRACT_TYPE, type.AssemblyQualifiedName);
                    TerasolunaException exception = new TerasolunaException(message);
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(message, exception);
                    }
                    throw exception;
                }
                if (type.ContainsGenericParameters)
                {
                    string message = string.Format(Properties.Resources.E_GENERIC_TYPE, type.AssemblyQualifiedName);
                    TerasolunaException exception = new TerasolunaException(message);
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(message, exception);
                    }
                    throw exception;
                }

                // インスタンスの生成
                result = GetInstanceByType<T>(type);
            }

            return result;
        }

        /// <summary>
        /// 指定したアセンブリ修飾名から、インスタンスを生成します。
        /// </summary>
        /// <para>
        /// <paramref name="typeName"/> の <see cref="Type"/> を生成します。
        /// <see cref="Type"/> が生成できない場合、例外をスローします。
        /// </para>1
        /// <para>
        /// <paramref name="typeName"/> の <see cref="Type"/> からインスタンスを生成します。
        /// インスタンスを生成できない場合、例外をスローします。
        /// </para>
        /// <para>
        /// 生成されたインスタンスを <typeparamref name="T"/> にキャストして返却します。
        /// <typeparamref name="T"/> にキャストできない場合、例外をスローします。
        /// </para>
        /// <typeparam name="T"> 生成したインスタンスを受ける型。</typeparam>
        /// <param name="typeName"> 取得したい型を表すアセンブリ修飾名。</param>
        /// <returns>生成された <typeparamref name="T"/> 型のインスタンス。</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="typeName"/> が null 参照です。
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="typeName"/> が空文字列です。
        /// </exception>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="typeName"/> の型を取得することができません。
        /// </item>
        /// <item>
        /// <paramref name="typeName"/> の型は抽象クラスです。
        /// </item>
        /// <item>
        /// <paramref name="typeName"/> の型はジェネリック型のクラスですが、型パラメータが設定されていません。
        /// </item>
        /// <item>
        /// <paramref name="typeName"/> の型のデフォルトコンストラクタを呼び出すことができません。
        /// </item>
        /// <item>
        /// <paramref name="typeName"/> の型のデフォルトコンストラクタで例外が発生しました。
        /// </item>
        /// <item>
        /// <paramref name="typeName"/> の型は型パラメータ <typeparamref name="T"/> にキャストできません。
        /// </item>
        /// </list>
        /// </exception>
        public static T CreateInstanceByTypeName<T>(string typeName) where T : class
        {
            // 入力チェック
            if (typeName == null)
            {
                ArgumentNullException exception = new ArgumentNullException("typeName");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(Properties.Resources.E_NULL_ARGUMENT, "typeName"), exception);
                }
                throw exception;
            }
            if (typeName.Length == 0)
            {
                string message = string.Format(Properties.Resources.E_EMPTY_STRING, "typeName");
                ArgumentException exception = new ArgumentException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }

            // 型の取得
            Type type = CreateTypeByTypeName(typeName);

            // 取得した型の確認
            if (type.IsAbstract)
            {
                string message = string.Format(Properties.Resources.E_ABSTRACT_TYPE, type.AssemblyQualifiedName);
                TerasolunaException exception = new TerasolunaException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }
            if (type.ContainsGenericParameters)
            {
                string message = string.Format(Properties.Resources.E_GENERIC_TYPE, type.AssemblyQualifiedName);
                TerasolunaException exception = new TerasolunaException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }

            // インスタンスの生成
            T result = GetInstanceByType<T>(type);

            return result;
        }

        /// <summary>
        /// 指定した型と指定した型に必要なジェネリック型のアセンブリ修飾名文字列からインスタンスを生成します。
        /// </summary>
        /// <typeparam name="T">
        /// 生成したインスタンスを受ける型。
        /// </typeparam>
        /// <param name="typeName">取得したい型を表すアセンブリ修飾名。</param>
        /// <param name="genericTypeNames"><paramref name="typeName"/> に必要な型パラメータを表す可変長引数。</param>
        /// <returns>生成された <typeparamref name="T"/> 型のインスタンス。</returns>
        /// <exception cref="ArgumentNullException">
        /// 以下のような場合に例外をスローします。 
        /// <list type="bullet">
        /// <item>
        /// <paramref name="typeName"/> が null 参照です。
        /// </item>
        /// <item>
        /// <paramref name="genericTypeNames"/> が null 参照です。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="ArgumentException">
        /// 以下のような場合に例外をスローします。 
        /// <list type="bullet">
        /// <item>
        /// <paramref name="typeName"/> が空文字列です。
        /// </item>
        /// <item>
        /// <paramref name="genericTypeNames"/> の配列内に null 参照または空文字列が含まれています。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// アセンブリ修飾名 <paramref name="typeName"/> の型を取得することができません。
        /// </item>
        /// <item>
        /// アセンブリ修飾名配列 <paramref name="genericTypeNames"/> 内に、型を取得することができないアセンブリ修飾名があります。
        /// </item>
        /// <item>
        /// <paramref name="genericTypeNames"/> に設定されているアセンブリ修飾名文字列配列数が、
        /// <paramref name="typeName"/> から生成された型に必要なジェネリック数より少ないです。
        /// </item>
        /// <item>
        /// <paramref name="genericTypeNames"/> に設定されているアセンブリ修飾名文字列配列内の型は、
        /// <paramref name="typeName"/> から生成された型に必要なジェネリックの制約を満たしていません。
        /// </item>
        /// <item>
        /// <paramref name="typeName"/> から生成した型は抽象クラスです。
        /// </item>
        /// <item>
        /// <paramref name="typeName"/> と <paramref name="genericTypeNames"/> から生成した型は
        /// ジェネリック型のクラスです。
        /// </item>
        /// <item>
        /// <paramref name="typeName"/> と <paramref name="genericTypeNames"/> から生成した型の
        /// デフォルトコンストラクタを呼び出すことができません。
        /// </item>
        /// <item>
        /// <paramref name="typeName"/> と <paramref name="genericTypeNames"/> から生成した型の
        /// デフォルトコンストラクタが例外をスローしました。
        /// </item>
        /// <item>
        /// <paramref name="typeName"/> と <paramref name="genericTypeNames"/> から生成した型の
        /// インスタンスが <typeparamref name="T"/> にキャストすることができません。
        /// </item>
        /// </list>
        /// </exception>
        public static T CreateInstanceWithGenericTypes<T>(
            string typeName, params string[] genericTypeNames) where T : class
        {
            // 入力チェック
            if (typeName == null)
            {
                ArgumentNullException exception = new ArgumentNullException("typeName");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(Properties.Resources.E_NULL_ARGUMENT, "typeName"), exception);
                }
                throw exception;
            }
            if (typeName.Length == 0)
            {
                string message = string.Format(Properties.Resources.E_EMPTY_STRING, "typeName");
                ArgumentException exception = new ArgumentException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }
            if(genericTypeNames == null)
            {
                ArgumentNullException exception = new ArgumentNullException("genericTypeNames");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(Properties.Resources.E_NULL_ARGUMENT, "genericTypeNames"), exception);
                }
                throw exception;
            }

            // 型を取得する。
            Type type = CreateTypeByTypeName(typeName);

            // 取得した型の確認
            if (type.IsAbstract)
            {
                string message = string.Format(Properties.Resources.E_PARAM_ABSTRACT_TYPE, "typeName", type.AssemblyQualifiedName);
                TerasolunaException exception = new TerasolunaException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }

            // 結果として利用する型。
            Type targetType = null;

            // ジェネリック型かどうかの確認
            if (type.IsGenericType)
            {
                int genericCount = type.GetGenericArguments().Length;
                if (genericCount > genericTypeNames.Length)
                {
                    string message = string.Format(
                        Properties.Resources.E_LESS_PARAMS, type.AssemblyQualifiedName, genericCount);
                    TerasolunaException exception = new TerasolunaException(message);
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(message, exception);
                    }
                    throw exception;
                }

                // ジェネリックパラメータが存在した場合、それを用いて構築されたジェネリック型を生成する。
                Type[] genericTypes = new Type[genericCount];
                for (int i = 0; i < genericCount; i++)
                {
                    if (genericTypeNames[i] == null)
                    {
                        string message = string.Format(Properties.Resources.E_NULL_ARGUMENT, "genericTypeNames[" + i + "]");
                        ArgumentException exception = new ArgumentException(message);
                        if (_log.IsErrorEnabled)
                        {
                            _log.Error(message, exception);
                        }
                        throw exception;
                    }
                    if (genericTypeNames[i].Length == 0)
                    {
                        string message = string.Format(Properties.Resources.E_EMPTY_STRING, "genericTypeNames[" + i + "]");
                        ArgumentException exception = new ArgumentException(message);
                        if (_log.IsErrorEnabled)
                        {
                            _log.Error(message, exception);
                        }
                        throw exception;
                    }

                    // 型の取得
                    Type genericType = CreateTypeByTypeName(genericTypeNames[i]);

                    // 型の確認
                    if (genericType.ContainsGenericParameters)
                    {
                        string message = string.Format(Properties.Resources.E_GENERIC_TYPE, genericType.AssemblyQualifiedName);
                        TerasolunaException exception = new TerasolunaException(message);
                        if (_log.IsErrorEnabled)
                        {
                            _log.Error(message, exception);
                        }
                        throw exception;
                    }

                    genericTypes[i] = genericType;
                }

                try
                {
                    targetType = type.MakeGenericType(genericTypes);
                }
                catch (ArgumentException e)
                {
                    string message = string.Format(
                        Properties.Resources.E_MAKE_GENERICTYPE_FAILED, type.AssemblyQualifiedName);
                    TerasolunaException exception = new TerasolunaException(message, e);
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(message, exception);
                    }
                    throw exception;
                }

                if (_log.IsDebugEnabled)
                {
                    _log.Debug(string.Format(Properties.Resources.D_MAKE_GENERICTYPE_SUCCESS, targetType.FullName));
                }
            }
            else
            {
                targetType = type;
            }

            // インスタンスの生成
            T result = GetInstanceByType<T>(targetType);

            return result;
        }

        /// <summary>
        /// 指定した型のインスタンスを生成します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// <paramref name="targetType"/> 型のインスタンスをリフレクションを利用してインスタンスを生成します。
        /// インスタンス化に失敗した場合、例外をスローします。
        /// </para>
        /// <para>
        /// 生成されたインスタンスを型パラメータ <typeparamref name="T"/> にキャストを行い、返却します。
        /// 型パラメータ <typeparamref name="T"/> にキャストできない場合、例外をスローします。
        /// </para>
        /// </remarks>
        /// <typeparam name="T"> 生成したインスタンスを受ける型。</typeparam>
        /// <param name="targetType"> 生成するインスタンスの型。</param>
        /// <returns>生成された <typeparamref name="T"/> 型のインスタンス。</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="targetType"/> が null 参照です。
        /// </exception>
        /// <exception cref="ArgumentException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="targetType"/> は抽象クラスです。
        /// </item>
        /// <item>
        /// <paramref name="targetType"/> はジェネリック型のクラスですが、型パラメータが設定されていません。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="targetType"/> のデフォルトコンストラクタを呼び出すことができません。
        /// </item>
        /// <item>
        /// <paramref name="targetType"/> のデフォルトコンストラクタが例外をスローしました。
        /// </item>
        /// <item>
        /// <paramref name="targetType"/> のインスタンスが <typeparamref name="T"/> にキャストすることができません。
        /// </item>
        /// </list>
        /// </exception>
        public static T CreateInstanceByType<T>(Type targetType) where T : class
        {
            // 入力チェック
            if (targetType == null)
            {
                ArgumentNullException exception = new ArgumentNullException("targetType");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(Properties.Resources.E_NULL_ARGUMENT, "targetType"), exception);
                }
                throw exception;
            }
            if (targetType.IsAbstract)
            {
                string message = string.Format(Properties.Resources.E_PARAM_ABSTRACT_TYPE, "targetType", targetType.AssemblyQualifiedName);
                ArgumentException exception = new ArgumentException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }
            if (targetType.ContainsGenericParameters)
            {
                string message = string.Format(Properties.Resources.E_PARAM_GENERIC_TYPE, "targetType", targetType.AssemblyQualifiedName);
                ArgumentException exception = new ArgumentException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }

            // インスタンスの生成
            T result = GetInstanceByType<T>(targetType);

            return result;
        }

        /// <summary>
        /// アセンブリ修飾名を表す文字列より、 <see cref="Type"/> インスタンスを取得します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// <paramref name="typeName"/>で指定されたアセンブリ修飾名の <see cref="Type"/> インスタンスを取得します。
        /// <paramref name="typeName"/>で指定されたアセンブリ修飾名が解決できない場合、例外をスローします。
        /// </para>
        /// </remarks>
        /// <param name="typeName">取得したい型を表すアセンブリ修飾名。</param>
        /// <returns>アセンブリ修飾名より解決された <see cref="Type"/> インスタンス。</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="typeName"/> が null 参照です。
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name  ="typeName"/> が空文字列です。
        /// </exception>
        /// <exception cref="TerasolunaException">
        /// <paramref name="typeName"/> の型を取得することができません。
        /// </exception>
        public static Type GetType(string typeName)
        {
            // 入力チェック
            if (typeName == null)
            {
                ArgumentNullException exception = new ArgumentNullException("typeName");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(Properties.Resources.E_NULL_ARGUMENT, "typeName"), exception);
                }
                throw exception;
            }
            if (typeName.Length == 0)
            {
                string message = string.Format(Properties.Resources.E_EMPTY_STRING, "typeName");
                ArgumentException exception = new ArgumentException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }

            // 型の取得
            Type type = CreateTypeByTypeName(typeName);

            return type;
        }

        /// <summary>
        /// 指定した型のインスタンスを生成します。
        /// </summary>
        /// <typeparam name="T">生成したインスタンスを受ける型。</typeparam>
        /// <param name="type">生成するインスタンスの型。</param>
        /// <returns>生成された <typeparamref name="T"/> 型のインスタンス。</returns>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="type"/> のデフォルトコンストラクタを呼び出すことができません。
        /// </item>
        /// <item>
        /// <paramref name="type"/> のデフォルトコンストラクタで例外が発生しました。
        /// </item>
        /// <item>
        /// <paramref name="type"/> は型パラメータ <typeparamref name="T"/> にキャストできません。
        /// </item>
        /// </list>
        /// </exception>
        private static T GetInstanceByType<T>(Type type) where T : class
        {
            // 型typeのインスタンスを生成する。
            // publicで引数を取らないコンストラクタが存在する必要がある。
            T result = null;
            try
            {
                result = Activator.CreateInstance(type) as T;
            }
            catch (MemberAccessException e)
            {
                string message = string.Format(
                    Properties.Resources.E_MEMBER_ACCESS_EXCEPTION,
                    type.AssemblyQualifiedName);
                TerasolunaException exception = new TerasolunaException(message, e);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }
            catch (TargetInvocationException e)
            {
                string message = string.Format(
                    Properties.Resources.E_TARGET_INVOCATION_EXCEPTION,
                    type.AssemblyQualifiedName);
                TerasolunaException exception = new TerasolunaException(message, e);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }

            // 生成した型が T にキャストできない場合
            if (result == null)
            {
                string message = string.Format(
                    Properties.Resources.E_INVALID_CAST_TYPE_FROM_GENERICPARAM,
                    type.AssemblyQualifiedName, typeof(T).AssemblyQualifiedName);
                TerasolunaException exception = new TerasolunaException(message);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }

            if (_log.IsDebugEnabled)
            {
                _log.Debug(string.Format(Properties.Resources.D_CREATE_INSTANCE_SUCCESS,
                    typeof(T).AssemblyQualifiedName, type.AssemblyQualifiedName));
            }

            return result;
        }

        /// <summary>
        /// アセンブリ修飾名の <see cref="Type"/> を取得します。
        /// </summary>
        /// <param name="typeName">アセンブリ修飾名。</param>
        /// <returns><paramref name="typeName"/> の <see cref="Type"/>。</returns>
        /// <exception cref="TerasolunaException">
        /// <paramref name="typeName"/> の型を取得することができません。
        /// </exception>
        private static Type CreateTypeByTypeName(string typeName)
        {
            // 型の取得
            Type type = null;
            try
            {
                type = Type.GetType(typeName, true);
            }
            catch (Exception e)
            {
                string message = string.Format(Properties.Resources.E_TYPE_LOAD_FAILED, typeName);
                TerasolunaException exception = new TerasolunaException(message, e);

                if (_log.IsErrorEnabled)
                {
                    _log.Error(message, exception);
                }
                throw exception;
            }

            if (_log.IsDebugEnabled)
            {
                _log.Debug(string.Format(Properties.Resources.D_TYPE_LOADED, type.AssemblyQualifiedName));
            }

            return type;
        }
    }

}
