#region License

/*
 * Copyright © 2002-2005 the original author or authors.
 *
 * 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.
 */

#endregion

#region Imports

using System;
using System.Collections;
#if NET_2_0
using System.Collections.ObjectModel;
#endif
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Runtime.CompilerServices;

#endregion

namespace Spring.Util
{
    /// <summary>
    /// Various reflection related methods that are missing from the standard library.
    /// </summary>
    /// <author>Rod Johnson</author>
    /// <author>Juergen Hoeller</author>
    /// <author>Aleksandar Seovic (.NET)</author>
    /// <author>Stan Dvoychenko (.NET)</author>
    /// <author>Bruno Baia (.NET)</author>
    public sealed class ReflectionUtils
    {
        /// <summary>
        /// Convenience <see cref="System.Reflection.BindingFlags"/> value that will
        /// match all private and public, static and instance members on a class
        /// in a case inSenSItivE fashion.
        /// </summary>
        public const BindingFlags AllMembersCaseInsensitiveFlags = BindingFlags.Public |
                                                                   BindingFlags.NonPublic | BindingFlags.Instance
                                                                   | BindingFlags.Static
                                                                   | BindingFlags.IgnoreCase;

        /// <summary>
        /// Avoid BeforeFieldInit problem
        /// </summary>
        static ReflectionUtils()
        {}

        /// <summary>
        /// Checks, if the specified type is a nullable
        /// </summary>
        public static bool IsNullableType(Type type)
        {
#if NET_2_0
            return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
#else
            return false;
#endif
        }

        /// <summary>
        /// Returns signature for the specified <see cref="System.Type"/>, method name and argument
        /// <see cref="System.Type"/>s.
        /// </summary>
        /// <param name="type">The <see cref="System.Type"/> the method is in.</param>
        /// <param name="method">The method name.</param>
        /// <param name="argumentTypes">
        /// The argument <see cref="System.Type"/>s.
        /// </param>
        /// <returns>The method signature.</returns>
        public static string GetSignature(
            Type type, string method, Type[] argumentTypes)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(type.FullName).Append("::").Append(method).Append("(");
            string separator = "";
            for (int i = 0; i < argumentTypes.Length; i++)
            {
                sb.Append(separator).Append(argumentTypes[i].FullName);
                separator = ",";
            }
            sb.Append(")");
            return sb.ToString();
        }


        /// <summary>
        /// Returns method for the specified <see cref="System.Type"/>, method
        /// name and argument
        /// <see cref="System.Type"/>s.
        /// </summary>
        /// <remarks>
        /// <para>Searches with BindingFlags</para>
        /// <para>When dealing with interface methods, you probable want to 'normalize' method references by calling 
        /// <see cref="MapInterfaceMethodToImplementationIfNecessary"/>. 
        /// </para>
        /// </remarks>
        /// <param name="targetType">
        /// The target <see cref="System.Type"/> to find the method on.
        /// </param>
        /// <param name="method">The method to find.</param>
        /// <param name="argumentTypes">
        /// The argument <see cref="System.Type"/>s. May be
        /// <see langword="null"/> if the method has no arguments.
        /// </param>
        /// <returns>The target method.</returns>
        /// <seealso cref="MapInterfaceMethodToImplementationIfNecessary"/>
        public static MethodInfo GetMethod(
            Type targetType, string method, Type[] argumentTypes)
        {
            AssertUtils.ArgumentNotNull(targetType, "Type must not be null");
            // try method exactly as specified first...
            MethodInfo retMethod = targetType.GetMethod(
                method,
                ReflectionUtils.AllMembersCaseInsensitiveFlags,
                null,
                argumentTypes == null ? Type.EmptyTypes : argumentTypes,
                null);

            if (retMethod == null)
            {
                // try explicit interface implementation...
                int idx = method.LastIndexOf('.');
                if (idx > -1)
                {
                    method = method.Substring(idx + 1);
                    retMethod = ReflectionUtils.GetMethod(targetType, method, argumentTypes);
                }
            }
            return retMethod;
        }

        /// <summary>
        /// Resolves a given <paramref name="methodInfo"/> to the <see cref="MethodInfo"/> representing the actual implementation.
        /// </summary>
        /// <remarks>
        /// see article <a href="http://weblog.ikvm.net/CommentView.aspx?guid=7356a87f-e5d7-4723-ae49-b263ab9e40ae">How To Get an Explicit Interface Implementation Method</a>.
        /// </remarks>
        /// <param name="methodInfo">a <see cref="MethodInfo"/></param>
        /// <param name="implementingType">the type to lookup</param>
        /// <returns>the <see cref="MethodInfo"/> representing the actual implementation method of the specified <paramref name="methodInfo"/></returns>
        public static MethodInfo MapInterfaceMethodToImplementationIfNecessary(MethodInfo methodInfo, System.Type implementingType)
        {
            AssertUtils.ArgumentNotNull(methodInfo, "methodInfo");
            AssertUtils.ArgumentNotNull(implementingType, "implementingType");
            AssertUtils.IsTrue(methodInfo.DeclaringType.IsAssignableFrom(implementingType), "methodInfo and implementingType are unrelated");

            MethodInfo concreteMethodInfo = methodInfo;

            if (methodInfo.DeclaringType.IsInterface)
            {
                InterfaceMapping interfaceMapping = implementingType.GetInterfaceMap(methodInfo.DeclaringType);
                int methodIndex = Array.IndexOf(interfaceMapping.InterfaceMethods, methodInfo);
                concreteMethodInfo = interfaceMapping.TargetMethods[methodIndex];
            }

            return concreteMethodInfo;
        }

        /// <summary>
        /// Returns an array of parameter <see cref="System.Type"/>s for the specified method
        /// or constructor.
        /// </summary>
        /// <param name="method">The method (or constructor).</param>
        /// <returns>An array containing the parameter <see cref="System.Type"/>s.</returns>
        /// <exception cref="System.ArgumentNullException">
        /// If <paramref name="method"/> is <see langword="null"/>.
        /// </exception>
        public static Type[] GetParameterTypes(MethodBase method)
        {
            AssertUtils.ArgumentNotNull(method, "method");
            return GetParameterTypes(method.GetParameters());
        }

        /// <summary>
        /// Returns an array of parameter <see cref="System.Type"/>s for the
        /// specified parameter info array.
        /// </summary>
        /// <param name="args">The parameter info array.</param>
        /// <returns>An array containing parameter <see cref="System.Type"/>s.</returns>
        /// <exception cref="System.ArgumentNullException">
        /// If <paramref name="args"/> is <see langword="null"/> or any of the
        /// elements <paramref name="args"/> is <see langword="null"/>.
        /// </exception>
        public static Type[] GetParameterTypes(ParameterInfo[] args)
        {
            AssertUtils.ArgumentNotNull(args, "args");
            Type[] types = new Type[args.Length];
            for (int i = 0; i < args.Length; i++)
            {
                types[i] = args[i].ParameterType;
            }
            return types;
        }

#if NET_2_0
        /// <summary>
        /// Returns an array of <see langword="string"/>s that represent 
        /// the names of the generic type parameter.
        /// </summary>
        /// <param name="method">The method.</param>
        /// <returns>An array containing the parameter names.</returns>
        /// <exception cref="System.ArgumentNullException">
        /// If <paramref name="method"/> is <see langword="null"/>.
        /// </exception>
        public static string[] GetGenericParameterNames(MethodInfo method)
        {
            AssertUtils.ArgumentNotNull(method, "method");
            return GetGenericParameterNames(method.GetGenericArguments());
        }

        /// <summary>
        /// Returns an array of <see langword="string"/>s that represent 
        /// the names of the generic type parameter.
        /// </summary>
        /// <param name="args">The parameter info array.</param>
        /// <returns>An array containing parameter names.</returns>
        /// <exception cref="System.ArgumentNullException">
        /// If <paramref name="args"/> is <see langword="null"/> or any of the
        /// elements <paramref name="args"/> is <see langword="null"/>.
        /// </exception>
        public static string[] GetGenericParameterNames(Type[] args)
        {
            AssertUtils.ArgumentNotNull(args, "args");
            string[] names = new string[args.Length];
            for (int i = 0; i < args.Length; i++)
            {
                names[i] = args[i].Name;
            }
            return names;
        }
#endif

        /// <summary>
        /// From a given list of methods, selects the method having an exact match on the given <paramref name="argValues"/>' types.
        /// </summary>
        /// <param name="methods">the list of methods to choose from</param>
        /// <param name="argValues">the arguments to the method</param>
        /// <returns>the method matching exactly the passed <paramref name="argValues"/>' types</returns>
        /// <exception cref="AmbiguousMatchException">
        /// If more than 1 matching methods are found in the <paramref name="methods"/> list.
        /// </exception>
        public static MethodInfo GetMethodByArgumentValues(MethodInfo[] methods, object[] argValues)
        {
            return (MethodInfo)GetMethodBaseByArgumentValues("method", methods, argValues);
        }

        /// <summary>
        /// From a given list of methods, selects the method having an exact match on the given <paramref name="argValues"/>' types.
        /// </summary>
        /// <param name="methodTypeName">the type of method (used for exception reporting only)</param>
        /// <param name="methods">the list of methods to choose from</param>
        /// <param name="argValues">the arguments to the method</param>
        /// <returns>the method matching exactly the passed <paramref name="argValues"/>' types</returns>
        /// <exception cref="AmbiguousMatchException">
        /// If more than 1 matching methods are found in the <paramref name="methods"/> list.
        /// </exception>
        private static MethodBase GetMethodBaseByArgumentValues(string methodTypeName, MethodBase[] methods,
                                                                object[] argValues)
        {
            MethodBase match = null;
            int matchCount = 0;

            foreach (MethodBase m in methods)
            {
                ParameterInfo[] parameters = m.GetParameters();
                bool isMatch = true;
                bool isExactMatch = true;
                object[] paramValues = (argValues == null) ? new object[0] : argValues;

                try
                {
                    if (parameters.Length > 0)
                    {
                        ParameterInfo lastParameter = parameters[parameters.Length - 1];
                        if (lastParameter.GetCustomAttributes(typeof(ParamArrayAttribute), false).Length > 0)
                        {
                            paramValues =
                                PackageParamArray(argValues, parameters.Length,
                                                  lastParameter.ParameterType.GetElementType());
                        }
                    }

                    if (parameters.Length != paramValues.Length)
                    {
                        isMatch = false;
                    }
                    else
                    {
                        for (int i = 0; i < parameters.Length; i++)
                        {
                            Type paramType = parameters[i].ParameterType;
                            object paramValue = paramValues[i];
                            if ((paramValue == null && paramType.IsValueType)
                                || (paramValue != null && !paramType.IsAssignableFrom(paramValue.GetType())))
                            {
                                isMatch = false;
                                break;
                            }
                            if (paramValue == null || paramType != paramValue.GetType())
                            {
                                isExactMatch = false;
                            }
                        }
                    }
                }
                catch (InvalidCastException)
                {
                    isMatch = false;
                }

                if (isMatch)
                {
                    if (isExactMatch)
                    {
                        return m;
                    }

                    matchCount++;
                    if (matchCount == 1)
                    {
                        match = m;
                    }
                    else
                    {
                        throw new AmbiguousMatchException(
                            string.Format("Ambiguous match for {0} '{1}' for the specified number and types of arguments.", methodTypeName,
                                          m.Name));
                    }
                }
            }

            return match;
        }

        /// <summary>
        /// From a given list of constructors, selects the constructor having an exact match on the given <paramref name="argValues"/>' types.
        /// </summary>
        /// <param name="methods">the list of constructors to choose from</param>
        /// <param name="argValues">the arguments to the method</param>
        /// <returns>the constructor matching exactly the passed <paramref name="argValues"/>' types</returns>
        /// <exception cref="AmbiguousMatchException">
        /// If more than 1 matching methods are found in the <paramref name="methods"/> list.
        /// </exception>
        public static ConstructorInfo GetConstructorByArgumentValues(ConstructorInfo[] methods, object[] argValues)
        {
            return (ConstructorInfo)GetMethodBaseByArgumentValues("constructor", methods, argValues);
        }


        /// <summary>
        /// Packages arguments into argument list containing parameter array as a last argument.
        /// </summary>
        /// <param name="argValues">Argument vaklues to package.</param>
        /// <param name="argCount">Total number of oarameters.</param>
        /// <param name="elementType">Type of the param array element.</param>
        /// <returns>Packaged arguments.</returns>
        public static object[] PackageParamArray(object[] argValues, int argCount, Type elementType)
        {
            object[] values = new object[argCount];
            int i = 0;

            // copy regular arguments
            while (i < argCount - 1)
            {
                values[i] = argValues[i];
                i++;
            }

            // package param array into last argument
            Array paramArray = Array.CreateInstance(elementType, argValues.Length - i);
            int j = 0;
            while (i < argValues.Length)
            {
                paramArray.SetValue(argValues[i++], j++);
            }
            values[values.Length - 1] = paramArray;

            return values;
        }

        /// <summary>
        /// Convenience method to convert an interface <see cref="System.Type"/> 
        /// to a <see cref="System.Type"/> array that contains 
        /// all the interfaces inherited and the specified interface.
        /// </summary>
        /// <param name="intf">The interface to convert.</param>
        /// <returns>An array of interface <see cref="System.Type"/>s.</returns>
        /// <exception cref="System.ArgumentException">
        /// If the <see cref="System.Type"/> specified is not an interface.
        /// </exception>
        /// <exception cref="System.ArgumentNullException">
        /// If <paramref name="intf"/> is <see langword="null"/>.
        /// </exception>
        public static Type[] ToInterfaceArray(Type intf)
        {
            AssertUtils.ArgumentNotNull(intf, "intf");

            if (!intf.IsInterface)
            {
                throw new ArgumentException(
                    string.Format(CultureInfo.InvariantCulture,
                                  "[{0}] is a class.",
                                  intf.FullName));
            }

            ArrayList interfaces = new ArrayList(intf.GetInterfaces());
            interfaces.Add(intf);

            return (Type[])interfaces.ToArray(typeof(Type));
        }

        /// <summary>
        /// Is the supplied <paramref name="propertyName"/> the default indexer for the
        /// supplied <paramref name="type"/>?
        /// </summary>
        /// <param name="propertyName">
        /// The name of the property on the supplied <paramref name="type"/> to be checked.
        /// </param>
        /// <param name="type">
        /// The <see cref="System.Type"/>  to be checked.
        /// </param>
        /// <returns>
        /// <see lang="true"/> if the supplied <paramref name="propertyName"/> is the
        /// default indexer for the supplied <paramref name="type"/>.
        /// </returns>
        /// <exception cref="System.NullReferenceException">
        /// If the supplied <paramref name="type"/> is <see langword="null"/>.
        /// </exception>
        public static bool PropertyIsIndexer(string propertyName, Type type)
        {
            DefaultMemberAttribute[] attribs =
                (DefaultMemberAttribute[])type.GetCustomAttributes(typeof(DefaultMemberAttribute), true);
            if (attribs.Length != 0)
            {
                foreach (DefaultMemberAttribute attrib in attribs)
                {
                    if (attrib.MemberName.Equals(propertyName))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Is the supplied <paramref name="method"/> declared on one of these interfaces?
        /// </summary>
        /// <param name="method">The method to check.</param>
        /// <param name="interfaces">The array of interfaces we want to check.</param>
        /// <returns>
        /// <see lang="true"/> if the method is declared on one of these interfaces.
        /// </returns>
        /// <exception cref="System.ArgumentException">
        /// If any of the <see cref="System.Type"/>s specified is not an interface.
        /// </exception>
        /// <exception cref="System.ArgumentNullException">
        /// If <paramref name="method"/> or any of the specified interfaces is
        /// <see langword="null"/>.
        /// </exception>
        public static bool MethodIsOnOneOfTheseInterfaces(MethodBase method, Type[] interfaces)
        {
            AssertUtils.ArgumentNotNull(method, "method");
            if (interfaces == null)
            {
                return false;
            }
            Type[] paramTypes = GetParameterTypes(method.GetParameters());
            for (int i = 0; i < interfaces.Length; i++)
            {
                Type interfaceType = interfaces[i];
                AssertUtils.ArgumentNotNull(interfaceType, StringUtils.Surround("interfaces[", i, "]"));
                if (!interfaceType.IsInterface)
                {
                    throw new ArgumentException(interfaces[i].FullName + " is not an interface");
                }
                try
                {
                    MethodInfo mi = interfaceType.GetMethod(
                        method.Name,
                        BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly,
                        null, paramTypes, null);
                    if (mi != null)
                    {
                        // found it...
                        return true;
                    }
                }
                catch
                {
                    // didn't find it, so keep going...
                }
            }
            return false;
        }

        /// <summary>
        /// Returns the default value for the specified <see cref="System.Type"/>
        /// </summary>
        /// <remarks>
        /// <p>
        /// Follows the standard .NET conventions for default values where
        /// relevant; for example, all numeric types default to the value
        /// <c>0</c>.
        /// </p>
        /// </remarks>
        /// <param name="type">
        /// The <see cref="System.Type"/> to return default value for.
        /// </param>
        /// <returns>
        /// The default value for the specified <see cref="System.Type"/>.
        /// </returns>
        /// <exception cref="System.ArgumentException">
        /// If the supplied <paramref name="type"/> is an enumerated type that
        /// has no values.
        /// </exception>
        public static object GetDefaultValue(Type type)
        {
            if (!type.IsValueType)
            {
                return null;
            }
            if (type == typeof(Boolean))
            {
                return false;
            }
            if (type == typeof(DateTime))
            {
                return DateTime.MinValue;
            }
            if (type == typeof(Char))
            {
                return Char.MinValue;
            }
            if (type.IsEnum)
            {
                Array values = Enum.GetValues(type);
                if (values == null || values.Length == 0)
                {
                    throw new ArgumentException("Bad 'enum' Type : cannot get default value because 'enum' has no values.");
                }
                return values.GetValue(0);
            }
            return 0;
        }

        /// <summary>
        /// Returns an array consisting of the default values for the supplied
        /// <paramref name="types"/>.
        /// </summary>
        /// <param name="types">
        /// The array of <see cref="System.Type"/>s to return default values for.
        /// </param>
        /// <returns>
        /// An array consisting of the default values for the supplied
        /// <paramref name="types"/>.
        /// </returns>
        /// <exception cref="System.ArgumentException">
        /// If any of the elements in the supplied <paramref name="types"/>
        /// array is an enumerated type that has no values.
        /// </exception>
        /// <seealso cref="Spring.Util.ReflectionUtils.GetDefaultValue(Type)"/>
        public static object[] GetDefaultValues(Type[] types)
        {
            object[] defaults = new object[types.Length];
            for (int i = 0; i < types.Length; ++i)
            {
                defaults[i] = GetDefaultValue(types[i]);
            }
            return defaults;
        }

        /// <summary>
        /// Checks that the parameter <see cref="System.Type"/>s of the
        /// supplied <paramref name="candidate"/> match the parameter
        /// <see cref="System.Type"/>s of the supplied
        /// <paramref name="parameterTypes"/>.
        /// </summary>
        /// <param name="candidate">The method to be checked.</param>
        /// <param name="parameterTypes">
        /// The array of parameter <see cref="System.Type"/>s to check against.
        /// </param>
        /// <returns>
        /// <see langword="true"/> if the parameter <see cref="System.Type"/>s
        /// match.
        /// </returns>
        public static bool ParameterTypesMatch(
            MethodInfo candidate, Type[] parameterTypes)
        {
            #region Sanity Checks

            AssertUtils.ArgumentNotNull(candidate, "candidate");
            AssertUtils.ArgumentNotNull(parameterTypes, "parameterTypes");

            #endregion

            Type[] candidatesParameterTypes
                = ReflectionUtils.GetParameterTypes(candidate);
            if (candidatesParameterTypes.Length != parameterTypes.Length)
            {
                return false;
            }
            for (int i = 0; i < candidatesParameterTypes.Length; ++i)
            {
                if (!candidatesParameterTypes[i].Equals(parameterTypes[i]))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Returns an array containing the <see cref="System.Type"/>s of the
        /// objects in the supplied array.
        /// </summary>
        /// <param name="args">
        /// The objects array for which the corresponding <see cref="System.Type"/>s
        /// are needed.
        /// </param>
        /// <returns>
        /// An array containing the <see cref="System.Type"/>s of the objects
        /// in the supplied array; this array will be empty (but not
        /// <see langword="null"/> if the supplied <paramref name="args"/>
        /// is null or has no elements.
        /// </returns>
        /// <example>
        /// <p>
        /// [C#]<br/>
        /// Given an array containing the following objects,
        /// <code>[83, "Foo", new object ()]</code>, the <see cref="System.Type"/>
        /// array returned from this method call would consist of the following
        /// <see cref="System.Type"/> elements...
        /// <code>[Int32, String, Object]</code>.
        /// </p>
        /// </example>
        public static Type[] GetTypes(object[] args)
        {
            if (args == null || args.Length == 0)
            {
                return Type.EmptyTypes;
            }
            Type[] paramsType = new Type[args.Length];
            for (int i = 0; i < args.Length; ++i)
            {
                object arg = args[i];
                paramsType[i] = (arg != null) ? args[i].GetType() : typeof(object);
            }
            return paramsType;
        }

        /// <summary>
        /// Does the given <see cref="System.Type"/> and/or it's superclasses
        /// have at least one or more methods with the given name (with any
        /// argument types)?
        /// </summary>
        /// <remarks>
        /// <p>
        /// Includes non-public methods in the methods searched.
        /// </p>
        /// </remarks>
        /// <param name="type">
        /// The <see cref="System.Type"/> to be checked.
        /// </param>
        /// <param name="name">
        /// The name of the method to be searched for. Case inSenSItivE.
        /// </param>
        /// <returns>
        /// <see langword="true"/> if the given <see cref="System.Type"/> or / and it's
        /// superclasses have at least one or more methods (with any argument types);
        /// <see langword="false"/> if not, or either of the parameters is <see langword="null"/>.
        /// </returns>
        public static bool HasAtLeastOneMethodWithName(Type type, string name)
        {
            if (type == null || StringUtils.IsNullOrEmpty(name))
            {
                return false;
            }
            return MethodCountForName(type, name) > 0;
        }

        /// <summary>
        ///  Within <paramref name="type"/>, counts the number of overloads for the method with the given (case-insensitive!) <paramref name="name"/> 
        /// </summary>
        /// <param name="type">The type to be searched</param>
        /// <param name="name">the name of the method for which overloads shall be counted</param>
        /// <returns>The number of overloads for method <paramref name="name"/> within type <paramref name="type"/></returns>
        public static int MethodCountForName(Type type, string name)
        {
            AssertUtils.ArgumentNotNull(type, "type", "Type must not be null");
            AssertUtils.ArgumentNotNull(name, "name", "Method name must not be null");
            MemberInfo[] methods = type.FindMembers(
                MemberTypes.Method,
                ReflectionUtils.AllMembersCaseInsensitiveFlags,
                new MemberFilter(ReflectionUtils.MethodNameFilter),
                name);
            return methods.Length;
        }

        private static bool MethodNameFilter(MemberInfo member, object criteria)
        {
            MethodInfo method = member as MethodInfo;
            string name = criteria as string;
            return String.Compare(method.Name, name, true, CultureInfo.InvariantCulture) == 0;
        }

        /// <summary>
        /// Creates a <see cref="System.Reflection.Emit.CustomAttributeBuilder"/>.
        /// </summary>
        /// <remarks>
        /// <p>
        /// Note that if a non-<see langword="null"/> <paramref name="sourceAttribute"/>
        /// is supplied, any read write properties exposed by the <paramref name="sourceAttribute"/>
        /// will be used to overwrite values that may have been passed in via the
        /// <paramref name="ctorArgs"/>. That is, the <paramref name="ctorArgs"/> will be used
        /// to initialize the custom attribute, and then any read-write properties on the
        /// <paramref name="sourceAttribute"/> will be plugged in.
        /// </p>
        /// </remarks>
        /// <param name="type">
        /// The desired <see cref="System.Attribute"/> <see cref="System.Type"/>.
        /// </param>
        /// <param name="ctorArgs">
        /// Any constructor arguments for the attribute (may be <see langword="null"/>
        /// in the case of no arguments).
        /// </param>
        /// <param name="sourceAttribute">
        /// Source attribute to copy properties from (may be <see langword="null"/>).
        /// </param>
        /// <returns>A custom attribute builder.</returns>
        /// <exception cref="System.ArgumentNullException">
        /// If the <paramref name="type"/> parameter is <see langword="null"/>.
        /// </exception>
        /// <exception cref="System.ArgumentNullException">
        /// If the <paramref name="type"/> parameter is not a <see cref="System.Type"/>
        /// that derives from the <see cref="System.Attribute"/> class.
        /// </exception>
        /// <seealso cref="System.Reflection.Emit.CustomAttributeBuilder"/>
        public static CustomAttributeBuilder CreateCustomAttribute(
            Type type, object[] ctorArgs, Attribute sourceAttribute)
        {
            #region Sanity Checks

            AssertUtils.ArgumentNotNull(type, "type");
            if (!typeof(Attribute).IsAssignableFrom(type))
            {
                throw new ArgumentException(
                    string.Format("[{0}] does not derive from the [System.Attribute] class.",
                                  type.FullName));
            }

            #endregion

            ConstructorInfo ci = type.GetConstructor(ReflectionUtils.GetTypes(ctorArgs));
            if (ci == null && ctorArgs.Length == 0)
            {
                ci = type.GetConstructors()[0];
                ctorArgs = GetDefaultValues(GetParameterTypes(ci.GetParameters()));
            }

            if (sourceAttribute != null)
            {
                object defaultAttribute = null;
                try
                {
                    defaultAttribute = ci.Invoke(ctorArgs);
                }
                catch
                {
                }

                IList getSetProps = new ArrayList();
                IList getSetValues = new ArrayList();
                IList readOnlyProps = new ArrayList();
                IList readOnlyValues = new ArrayList();
                foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
                {
                    if (pi.DeclaringType == typeof(Attribute))
                        continue;

                    if (pi.CanRead)
                    {
                        if (pi.CanWrite)
                        {
                            object propValue = pi.GetValue(sourceAttribute, null);
                            if (defaultAttribute != null)
                            {
                                object defaultValue = pi.GetValue(defaultAttribute, null);
                                if ((propValue == null && defaultValue == null) ||
                                    (propValue != null && propValue.Equals(defaultValue)))
                                    continue;
                            }
                            getSetProps.Add(pi);
                            getSetValues.Add(propValue);
                        }
                        else
                        {
                            readOnlyProps.Add(pi);
                            readOnlyValues.Add(pi.GetValue(sourceAttribute, null));
                        }
                    }
                }

                if (readOnlyProps.Count == 1)
                {
                    PropertyInfo pi = readOnlyProps[0] as PropertyInfo;
                    ConstructorInfo ciTemp = type.GetConstructor(new Type[1] { pi.PropertyType });
                    if (ciTemp != null)
                    {
                        ci = ciTemp;
                        ctorArgs = new object[1] { readOnlyValues[0] };
                    }
                    else
                    {
                        ciTemp = type.GetConstructor(new Type[1] { readOnlyValues[0].GetType() });
                        if (ciTemp != null)
                        {
                            ci = ciTemp;
                            ctorArgs = new object[1] { readOnlyValues[0] };
                        }
                    }
                }

                PropertyInfo[] propertyInfos = new PropertyInfo[getSetProps.Count];
                getSetProps.CopyTo(propertyInfos, 0);

                object[] propertyValues = new object[getSetValues.Count];
                getSetValues.CopyTo(propertyValues, 0);

                return new CustomAttributeBuilder(ci, ctorArgs, propertyInfos, propertyValues);
            }
            else
            {
                return new CustomAttributeBuilder(ci, ctorArgs);
            }
        }

        /// <summary>
        /// Creates a <see cref="System.Reflection.Emit.CustomAttributeBuilder"/>.
        /// </summary>
        /// <param name="type">
        /// The desired <see cref="System.Attribute"/> <see cref="System.Type"/>.
        /// </param>
        /// <param name="sourceAttribute">
        /// Source attribute to copy properties from (may be <see langword="null"/>).
        /// </param>
        /// <returns>A custom attribute builder.</returns>
        public static CustomAttributeBuilder CreateCustomAttribute(
            Type type, Attribute sourceAttribute)
        {
            return CreateCustomAttribute(type, new object[] { }, sourceAttribute);
        }

        /// <summary>
        /// Creates a <see cref="System.Reflection.Emit.CustomAttributeBuilder"/>.
        /// </summary>
        /// <param name="sourceAttribute">
        /// The source attribute to copy properties from.
        /// </param>
        /// <returns>A custom attribute builder.</returns>
        /// <exception cref="System.NullReferenceException">
        /// If the supplied <paramref name="sourceAttribute"/> is
        /// <see langword="null"/>.
        /// </exception>
        public static CustomAttributeBuilder CreateCustomAttribute(Attribute sourceAttribute)
        {
            return CreateCustomAttribute(sourceAttribute.GetType(), sourceAttribute);
        }

        /// <summary>
        /// Creates a <see cref="System.Reflection.Emit.CustomAttributeBuilder"/>.
        /// </summary>
        /// <param name="type">
        /// The desired <see cref="System.Attribute"/> <see cref="System.Type"/>.
        /// </param>
        /// <returns>A custom attribute builder.</returns>
        public static CustomAttributeBuilder CreateCustomAttribute(Type type)
        {
            return CreateCustomAttribute(type, new object[] { }, null);
        }

        /// <summary>
        /// Creates a <see cref="System.Reflection.Emit.CustomAttributeBuilder"/>.
        /// </summary>
        /// <param name="type">
        /// The desired <see cref="System.Attribute"/> <see cref="System.Type"/>.
        /// </param>
        /// <param name="ctorArgs">
        /// Any constructor arguments for the attribute (may be <see langword="null"/>
        /// in the case of no arguments).
        /// </param>
        /// <returns>A custom attribute builder.</returns>
        public static CustomAttributeBuilder CreateCustomAttribute(
            Type type, params object[] ctorArgs)
        {
            return CreateCustomAttribute(type, ctorArgs, null);
        }

#if NET_2_0
        /// <summary>
        /// Creates a <see cref="System.Reflection.Emit.CustomAttributeBuilder"/>.
        /// </summary>
        /// <param name="attributeData">
        /// The <see cref="System.Reflection.CustomAttributeData"/> to create 
        /// the custom attribute builder from.
        /// </param>
        /// <returns>A custom attribute builder.</returns>
        public static CustomAttributeBuilder CreateCustomAttribute(CustomAttributeData attributeData)
        {
            object[] parameterValues = new object[attributeData.ConstructorArguments.Count];
            Type[] parameterTypes = new Type[attributeData.ConstructorArguments.Count];

            IList namedParameterValues = new ArrayList();
            IList namedFieldValues = new ArrayList();

            // Fill arrays of the constructor parameters
            for (int i = 0; i < attributeData.ConstructorArguments.Count; i++)
            {
                parameterTypes[i] = attributeData.ConstructorArguments[i].ArgumentType;
                parameterValues[i] = ConvertValueIfNecessary(attributeData.ConstructorArguments[i].Value);
            }

            Type attributeType = attributeData.Constructor.DeclaringType;
            PropertyInfo[] attributeProperties = attributeType.GetProperties(
                BindingFlags.Instance | BindingFlags.Public);
            FieldInfo[] attributeFields = attributeType.GetFields(
                BindingFlags.Instance | BindingFlags.Public);

            // Not using generics bellow as probably Spring.NET tries to keep 
            // it on .NET1 compatibility level right now I believe (SD)
            // In case of using List<CustomAttributesData> the above note makes
            // no sense (SD:)
            IList propertiesToSet = new ArrayList();
            int k = 0;

            IList fieldsToSet = new ArrayList();
            int n = 0;


            // Fills arrays of the constructor named parameters
            foreach (CustomAttributeNamedArgument namedArgument in attributeData.NamedArguments)
            {
                bool noMatchingProperty = false;

                // Now iterate through all of the PropertyInfo, find the
                // one with the corresponding to the NamedProperty name
                // and add it to the array of properties to set.
                for (int j = 0; j < attributeProperties.Length; j++)
                {
                    if (attributeProperties[j].Name == namedArgument.MemberInfo.Name)
                    {
                        propertiesToSet.Add(attributeProperties[j]);
                        namedParameterValues.Add(ConvertValueIfNecessary(namedArgument.TypedValue.Value));
                        break;
                    }
                    else
                    {
                        if (j == attributeProperties.Length - 1)
                        {
                            // In case of no match, throw
                            noMatchingProperty = true;
                            /*
                            throw new InvalidOperationException(
                                String.Format(CultureInfo.InvariantCulture,
                                "The property with name {0} can't be found in the " +
                                "type {1}, but is present as a named property " +
                                "on the attributeData {2}", namedArgument.MemberInfo.Name,
                                attributeType.FullName, attributeData));
                            */
                        }
                    }
                }
                if (noMatchingProperty)
                {
                    for (int j = 0; j < attributeFields.Length; j++)
                    {
                        if (attributeFields[j].Name == namedArgument.MemberInfo.Name)
                        {
                            fieldsToSet.Add(attributeFields[j]);
                            namedFieldValues.Add(ConvertValueIfNecessary(namedArgument.TypedValue.Value));
                            break;
                        }
                        else
                        {
                            if (j == attributeFields.Length - 1)
                            {
                                throw new InvalidOperationException(
                                        String.Format(CultureInfo.InvariantCulture,
                                        "A property or public field with name {0} can't be found in the " +
                                        "type {1}, but is present as a named property " +
                                        "on the attributeData {2}", namedArgument.MemberInfo.Name,
                                        attributeType.FullName, attributeData));
                            }
                        }
                    }
                }
            }
            // Get constructor corresponding to the parameters and their types
            ConstructorInfo constructor = attributeType.GetConstructor(parameterTypes);

            PropertyInfo[] namedProperties = new PropertyInfo[propertiesToSet.Count];
            propertiesToSet.CopyTo(namedProperties, 0);

            object[] propertyValues = new object[namedParameterValues.Count];
            namedParameterValues.CopyTo(propertyValues, 0);

            if (fieldsToSet.Count == 0)
            {
                return new CustomAttributeBuilder(
                        constructor, parameterValues, namedProperties, propertyValues);
            }
            else
            {
                FieldInfo[] namedFields = new FieldInfo[fieldsToSet.Count];
                fieldsToSet.CopyTo(namedFields, 0);

                object[] fieldValues = new object[namedFieldValues.Count];
                namedFieldValues.CopyTo(fieldValues, 0);

                return new CustomAttributeBuilder(
                        constructor, parameterValues, namedProperties, propertyValues, namedFields, fieldValues);
            }



        }

        private static object ConvertValueIfNecessary(object value)
        {
            if (value == null)
                return value;

            // We are only hunting for the case of the ReadOnlyCollection<T> here.
            ReadOnlyCollection<CustomAttributeTypedArgument> sourceArray =
                value as ReadOnlyCollection<CustomAttributeTypedArgument>;

            if (sourceArray == null)
                return value;

            Type underlyingType = null; // type to be used for arguments
            Array returnArray = null;
            for (int i = 0; i < sourceArray.Count; i++)
            {
                if (underlyingType == null)
                {
                    underlyingType = sourceArray[i].ArgumentType;
                    returnArray = Array.CreateInstance(underlyingType, sourceArray.Count);
                }
                if (!underlyingType.Equals(sourceArray[i].ArgumentType))
                {
                    throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
                        "Types for the same named parameter of array type are expected to be same"));
                }

                returnArray.SetValue(sourceArray[i].Value, i);
            }

            return returnArray;

        }
#endif

        /// <summary>
        /// Tries to find matching methods in the specified <see cref="System.Type"/>
        /// for each method in the supplied <paramref name="methods"/> list.
        /// </summary>
        /// <param name="type">
        /// The <see cref="System.Type"/> to look for matching methods in.
        /// </param>
        /// <param name="methods">The methods to match.</param>
        /// <param name="strict">
        /// A flag that specifies whether to throw an exception if a matching
        /// method is not found.
        /// </param>
        /// <returns>A list of the matched methods.</returns>
        /// <exception cref="System.ArgumentNullException">
        /// If either of the <paramref name="type"/> or
        /// <paramref name="methods"/> parameters are <see langword="null"/>.
        /// </exception>
        public static MethodInfo[] GetMatchingMethods(Type type, MethodInfo[] methods, bool strict)
        {
            AssertUtils.ArgumentNotNull(type, "type");
            AssertUtils.ArgumentNotNull(methods, "methods");

            BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;

            MethodInfo[] matched = new MethodInfo[methods.Length];
            for (int i = 0; i < methods.Length; i++)
            {
                MethodInfo method = methods[i];
                MethodInfo match = type.GetMethod(method.Name, flags, null, ReflectionUtils.GetParameterTypes(method), null);
                if ((match == null || match.ReturnType != method.ReturnType) && strict)
                {
                    throw new Exception(
                        string.Format("Method '{0}' could not be matched in the target class [{1}].",
                                      method.Name, type.FullName));
                }
                matched[i] = match;
            }
            return matched;
        }

        /// <summary>
        /// Returns the <see cref="System.Type"/> of the supplied
        /// <paramref name="source"/>.
        /// </summary>
        /// <remarks>
        /// <p>
        /// If the <paramref name="source"/> is a <see cref="System.Type"/>
        /// instance, the return value of this method call with be the
        /// <paramref name="source"/> parameter cast to a
        /// <see cref="System.Type"/>. If the <paramref name="source"/> is
        /// anything other than a <see cref="System.Type"/>, the return value
        /// will be the result of invoking the <paramref name="source"/>'s
        /// <see cref="System.Object.GetType()"/> method.
        /// </p>
        /// </remarks>
        /// <param name="source">
        /// A <see cref="Type"/> or <see cref="object"/> instance.
        /// </param>
        /// <returns>
        /// The <paramref name="source"/>argument if it is a
        /// <see cref="Type"/> or the result of invoking
        /// <see cref="Object.GetType"/> on the argument if it
        /// is an <see cref="Object"/>.
        /// </returns>
        /// <exception cref="System.NullReferenceException">
        /// If the <paramref name="source"/> is <see langword="null"/>.
        /// </exception>
        public static Type TypeOfOrType(object source)
        {
            return source is Type ? source as Type : source.GetType();
        }


#if NET_2_0
        private static readonly MethodInfo Exception_InternalPreserveStackTrace =
            typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
#else
        private static readonly FieldInfo Exception_RemoteStackTraceString = 
            typeof(Exception).GetField("_remoteStackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
#endif

        /// <summary>
        /// Unwraps the supplied <see cref="System.Reflection.TargetInvocationException"/> 
        /// and returns the inner exception preserving the stack trace.
        /// </summary>
        /// <param name="ex">
        /// The <see cref="System.Reflection.TargetInvocationException"/> to unwrap.
        /// </param>
        /// <returns>The unwrapped exception.</returns>
        public static Exception UnwrapTargetInvocationException(TargetInvocationException ex)
        {
#if NET_2_0
            Exception_InternalPreserveStackTrace.Invoke(ex.InnerException, new Object[] { });
#else
            Exception_RemoteStackTraceString.SetValue(ex.InnerException, ex.InnerException.StackTrace + Environment.NewLine);
#endif
            return ex.InnerException;
        }

        /// <summary>
        /// Is the supplied <paramref name="type"/> can be accessed outside the assembly ?
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>
        /// <see langword="true"/> if the type can be accessed outside the assembly;
        /// Otherwise <see langword="false"/>.
        /// </returns>
        public static bool IsTypeVisible(Type type)
        {
            return IsTypeVisible(type, null);
        }

        /// <summary>
        /// Is the supplied <paramref name="type"/> can be accessed 
        /// from the supplied friendly assembly ?
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <param name="friendlyAssemblyName">The friendly assembly name.</param>
        /// <returns>
        /// <see langword="true"/> if the type can be accessed 
        /// from the supplied friendly assembly; Otherwise <see langword="false"/>.
        /// </returns>
        public static bool IsTypeVisible(Type type, string friendlyAssemblyName)
        {
#if NET_2_0
            if (type.IsVisible)
            {
                return true;
            }
            else
            {
                if (friendlyAssemblyName != null
                    && friendlyAssemblyName.Length > 0
                    && (!type.IsNested || type.IsNestedPublic ||
                     (!type.IsNestedPrivate && (type.IsNestedAssembly || type.IsNestedFamORAssem))))
                {
                    object[] attrs = type.Assembly.GetCustomAttributes(typeof(InternalsVisibleToAttribute), false);
                    foreach (InternalsVisibleToAttribute ivta in attrs)
                    {
                        if (ivta.AssemblyName == friendlyAssemblyName)
                        {
                            return true;
                        }
                    }
                }
            }
#else
            if (type.IsPublic || (type.IsNestedPublic && type.DeclaringType.IsPublic))
            {
                return true;
            }
#endif
            return false;
        }

        /// <summary>
        /// Gets all of the interfaces implemented by 
        /// the specified <see cref="System.Type"/>.
        /// </summary>
        /// <param name="type">
        /// The object to get the interfaces of.
        /// </param>
        /// <returns>
        /// All of the interfaces implemented by the
        /// <see cref="System.Type"/>.
        /// </returns>
        public static Type[] GetInterfaces(Type type)
        {
            AssertUtils.ArgumentNotNull(type, "type");

            if (type.IsInterface)
            {
                ArrayList interfaces = new ArrayList();
                interfaces.Add(type);
                interfaces.AddRange(type.GetInterfaces());
                return (Type[])interfaces.ToArray(typeof(Type));
            }
            else
            {
                return type.GetInterfaces();
            }
        }

        /// <summary>
        /// Returns the explicit <see cref="System.Exception"/> that is the root cause of an exception.
        /// </summary>
        /// <remarks>
        /// If the InnerException property of the current exception is a null reference 
        /// or a <see cref="System.NullReferenceException"/>, returns the current exception.
        /// </remarks>
        /// <param name="ex">The last exception thrown.</param>
        /// <returns>
        /// The first explicit exception thrown in a chain of exceptions.
        /// </returns>
        public static Exception GetExplicitBaseException(Exception ex)
        {
            Exception innerEx = ex.InnerException;
            while (innerEx != null &&
                !(innerEx is NullReferenceException))
            {
                ex = innerEx;
                innerEx = innerEx.InnerException;
            }
            return ex;
        }

        /// <summary>
        /// Copies all fields from one object to another. 
        /// </summary>
        /// <remarks>
        /// The types of both objects must be related. This means, that either of the following is true:
        /// <list type="bullet">
        ///		<item><description>fromObject.GetType() == toObject.GetType()</description></item>
        ///		<item><description>fromObject.GetType() is derived from toObject.GetType()</description></item>
        ///		<item><description>toObject.GetType() is derived from fromObject.GetType()</description></item>
        /// </list>
        /// </remarks>
        /// <param name="fromObject">The source object</param>
        /// <param name="toObject">The object, who's fields will be populated with values from the source object</param>
        /// <exception cref="ArgumentException">If the object's types are not related</exception>
        public static void MemberwiseCopy(object fromObject, object toObject)
        {
            Type fromType = fromObject.GetType();
            Type toType = toObject.GetType();

            Type smallerType;

            if (fromType.IsAssignableFrom(toType))
            {
                smallerType = fromType;
            }
            else if (toType.IsAssignableFrom(fromType))
            {
                smallerType = toType;
            }
            else
            {
                throw new ArgumentException("object types are not related");
            }

            MemberwiseCopyInternal(fromObject, toObject, smallerType);
        }

#if NET_2_0
        private static void MemberwiseCopyInternal(object fromObject, object toObject, Type smallerType)
        {
            MemberwiseCopyHandler impl = GetImpl(smallerType);
            impl(fromObject, toObject);
        }

        private delegate void MemberwiseCopyHandler(object a, object b);

        private static readonly Hashtable s_handlerCache = new Hashtable();

        private static MemberwiseCopyHandler GetImpl(Type type)
        {
            MemberwiseCopyHandler handler = s_handlerCache[type] as MemberwiseCopyHandler;
            if (handler != null)
                return handler;

            lock (s_handlerCache)
            {
                handler = s_handlerCache[type] as MemberwiseCopyHandler;
                if (handler != null)
                    return handler;

                FieldInfo[] fields = GetFields(type);
                SecurityCritical.ExecutePrivileged(new PermissionSet(PermissionState.Unrestricted), delegate
                {
                    DynamicMethod dm = new DynamicMethod(type.FullName + ".ShallowCopy", null, new Type[] { typeof(object), typeof(object) }, type.Module, true);
                    ILGenerator ilGen = dm.GetILGenerator();
                    ilGen.DeclareLocal(type);
                    ilGen.DeclareLocal(type);
                    ilGen.Emit(OpCodes.Ldarg_0);
                    ilGen.Emit(OpCodes.Castclass, type);
                    ilGen.Emit(OpCodes.Stloc_0);
                    ilGen.Emit(OpCodes.Ldarg_1);
                    ilGen.Emit(OpCodes.Castclass, type);
                    ilGen.Emit(OpCodes.Stloc_1);

                    foreach (FieldInfo field in fields)
                    {
                        ilGen.Emit(OpCodes.Ldloc_1);
                        ilGen.Emit(OpCodes.Ldloc_0);
                        ilGen.Emit(OpCodes.Ldfld, field);
                        ilGen.Emit(OpCodes.Stfld, field);
                    }
                    ilGen.Emit(OpCodes.Ret);

                    handler = (MemberwiseCopyHandler)dm.CreateDelegate(typeof(MemberwiseCopyHandler));
                });

                s_handlerCache[type] = handler;
            }
            return handler;
        }
#else
        private static void MemberwiseCopyInternal(object fromObject, object toObject, Type smallerType)
        {
            FieldInfo[] fields = GetFields(smallerType);
            for (int i = 0; i < fields.Length; i++)
            {
                FieldInfo field = fields[i];
                field.SetValue(toObject, field.GetValue(fromObject));
            }
        }
#endif

        #region Field Cache Management for "MemberwiseCopy"

        private const BindingFlags FIELDBINDINGS =
            BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic;

        private static readonly Hashtable s_fieldCache = new Hashtable();

        private static FieldInfo[] GetFields(Type type)
        {
            lock (s_fieldCache)
            {
                FieldInfo[] fields = (FieldInfo[])s_fieldCache[type];
                if (fields == null)
                {
                    ArrayList fieldList = new ArrayList();
                    CollectFieldsRecursive(type, fieldList);
                    fields = (FieldInfo[])fieldList.ToArray(typeof(FieldInfo));
                    s_fieldCache[type] = fields;
                }
                return fields;
            }
        }

        private static void CollectFieldsRecursive(Type type, ArrayList fieldList)
        {
            if (type == typeof(object))
                return;

            FieldInfo[] fields = type.GetFields(FIELDBINDINGS);
            fieldList.AddRange(fields);
            CollectFieldsRecursive(type.BaseType, fieldList);
        }

        #endregion Field Cache Management for "MemberwiseCopy"


        #region CustomAttributeBuilderBuilder inner class definition

        /// <summary>
        /// Creates a <see cref=" CustomAttributeBuilder"/>.
        /// </summary>
        /// <author>Bruno Baia</author>
        public class CustomAttributeBuilderBuilder
        {
            #region Fields

            private Type type;
            private ArrayList constructorArgs;
            private ArrayList namedProperties;
            private ArrayList propertyValues;

            #endregion

            #region Constructor(s) / Destructor

            /// <summary>
            /// Creates a new instance of the 
            /// <see cref="CustomAttributeBuilderBuilder"/> class.
            /// </summary>
            /// <param name="attributeType">The custom attribute type.</param>
            public CustomAttributeBuilderBuilder(Type attributeType)
                :
                this(attributeType, ObjectUtils.EmptyObjects)
            {
            }

            /// <summary>
            /// Creates a new instance of the 
            /// <see cref="CustomAttributeBuilderBuilder"/> class.
            /// </summary>
            /// <param name="attributeType">The custom attribute type.</param>
            /// <param name="constructorArgs">The custom attribute constructor arguments.</param>
            public CustomAttributeBuilderBuilder(Type attributeType, params object[] constructorArgs)
            {
                AssertUtils.ArgumentNotNull(attributeType, "attributeType");
                if (!typeof(Attribute).IsAssignableFrom(attributeType))
                {
                    throw new ArgumentException(
                        string.Format("[{0}] does not derive from the [System.Attribute] class.",
                                      attributeType.FullName));
                }
                this.type = attributeType;
                this.constructorArgs = new ArrayList(constructorArgs);
                this.namedProperties = new ArrayList();
                this.propertyValues = new ArrayList();
            }

            #endregion

            #region Public Methods

            /// <summary>
            /// Adds the specified values to the constructor argument list 
            /// used to create the custom attribute.
            /// </summary>
            /// <param name="values">An array of argument values.</param>
            public void AddContructorArgument(params object[] values)
            {
                this.constructorArgs.AddRange(values);
            }

            /// <summary>
            /// Adds a property value to the custom attribute.
            /// </summary>
            /// <param name="name">The property name.</param>
            /// <param name="value">The property value.</param>
            public void AddPropertyValue(string name, object value)
            {
                PropertyInfo propertyInfo = this.type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
                if (propertyInfo == null)
                {
                    throw new ArgumentException(
                        String.Format("The property '{0}' does no exist in the attribute '{1}'.", name, this.type));
                }

                this.namedProperties.Add(propertyInfo);
                this.propertyValues.Add(value);
            }

            /// <summary>
            /// Creates the <see cref="CustomAttributeBuilderBuilder"/>.
            /// </summary>
            /// <returns>The created <see cref="CustomAttributeBuilderBuilder"/>.</returns>
            public CustomAttributeBuilder Build()
            {
                object[] caArray = (object[])this.constructorArgs.ToArray(typeof(object));
                ConstructorInfo ci = this.type.GetConstructor(ReflectionUtils.GetTypes(caArray));
                if (ci == null && caArray.Length == 0)
                {
                    ci = this.type.GetConstructors()[0];
                    caArray = ReflectionUtils.GetDefaultValues(ReflectionUtils.GetParameterTypes(ci.GetParameters()));
                }

                if (namedProperties.Count > 0)
                {
                    PropertyInfo[] npArray = (PropertyInfo[])this.namedProperties.ToArray(typeof(PropertyInfo));
                    object[] pvArray = (object[])this.propertyValues.ToArray(typeof(object));
                    return new CustomAttributeBuilder(ci, caArray, npArray, pvArray);
                }
                else
                {
                    return new CustomAttributeBuilder(ci, caArray);
                }

            }

            #endregion
        }

        #endregion
    }
}