/// <summary>
 /// Inserts a MethodCallDefinition instance into the method calls list at specified index.
 /// </summary>
 /// <param name="index">The zero-based index at which MethodCallDefinition instance should be inserted.</param>
 /// <param name="methodCallDefinition">The MethodCallDefinition instance to insert</param>
 /// <exception cref="ArgumentNullException">If the MethodCallDefinition instance to insert is null</exception>
 /// <exception cref="ArgumentOutOfRangeException">If index is less than zero or index is greater than Count
 /// of method calls list</exception>
 public void InsertMethodCallDefinition(int index, MethodCallDefinition methodCallDefinition)
 {
     Helper.ValidateNotNull(methodCallDefinition, "methodCallDefinition");
     methodCalls.Insert(index, methodCallDefinition);
 }
 /// <summary>
 /// Removes a MethodCallDefinition instance from the method calls list.
 /// </summary>
 /// <param name="methodCallDefinition">The MethodCallDefinition instance to remove</param>
 /// <exception cref="ArgumentNullException">If the MethodCallDefinition instance to remove is null</exception>
 public void RemoveMethodCallDefinition(MethodCallDefinition methodCallDefinition)
 {
     Helper.ValidateNotNull(methodCallDefinition, "methodCallDefinition");
     methodCalls.Remove(methodCallDefinition);
 }
 /// <summary>
 /// <p>Creates a new instance of ObjectDefinition.</p>
 /// </summary>
 ///
 /// <p>
 /// Version 1.2: Move part of parameter verification to property setter.
 /// </p>
 ///
 /// <param name="appDomain">The application domain (can be null or empty).</param>
 /// <param name="assembly">The assembly name (can be null if appDomain is null or empty).</param>
 /// <param name="typeName">The type name (cannot be null).</param>
 /// <param name="ignoreCase">
 /// False for case sensitive lookup, true for case insensitive lookup of the static method name.
 /// </param>
 /// <param name="paramTypes">
 /// The types of the parameters (can be null or empty).
 /// </param>
 /// <param name="paramValues">
 /// The values of the parameters (can be null or empty).
 /// </param>
 /// <param name="methodCalls">
 /// The method calls definitions to be made on the object after it is create (can be null).
 /// </param>
 /// <param name="instantiationLifetime">
 /// InstantiationLifetime value stating the length of the lifetime of this instance.
 /// </param>
 /// <param name="methodName">The name of the static method to use to create the object.</param>
 ///
 /// <exception cref="ArgumentNullException">
 /// If typeName or methodName is null or an array element (besides an element in paramValues
 /// corresponding to a paramType of "null") is null.
 /// </exception>
 /// <exception cref="ArgumentException">
 /// If the paramTypes and paramValues arrays don't have the same size and aren't both null or
 /// if paramTypes has invalid values (see ParamTypes), or assembly is the empty string
 /// or instantiationLifetime is invalid or methodName is empty.
 /// </exception>
 /// <since>1.1</since>
 private ObjectDefinition(string appDomain, string assembly, string typeName,
     string methodName, bool ignoreCase, InstantiationLifetime instantiationLifetime,
     string[] paramTypes, object[] paramValues, MethodCallDefinition[] methodCalls)
     : base(ignoreCase, paramTypes, paramValues, methodName)
 {
     this.AppDomain = appDomain;
     this.Assembly = assembly;
     this.TypeName = typeName;
     this.InstantiationLifetime = instantiationLifetime;
     this.MethodCalls = methodCalls;
 }
 /// <summary>
 /// Adds a MethodCallDefinition instance to method calls list.
 /// </summary>
 /// <returns>The position into which the new MethodCallDefinition instance was inserted.</returns>
 ///
 /// <param name="methodCallDefinition">The MethodCallDefinition instance to add</param>
 /// <exception cref="ArgumentNullException">If the MethodCallDefinition instance to add is null</exception>
 public int AddMethodCallDefinition(MethodCallDefinition methodCallDefinition)
 {
     Helper.ValidateNotNull(methodCallDefinition, "methodCallDefinition");
     return methodCalls.Add(methodCallDefinition);
 }
 /// <summary>
 /// <p>Creates a new instance of ObjectDefinition.</p>
 /// </summary>
 ///
 /// <param name="appDomain">The application domain (can be null or empty).</param>
 /// <param name="assembly">The assembly name (can be null if appDomain is null or empty).</param>
 /// <param name="typeName">The type name (cannot be null).</param>
 /// <param name="ignoreCase">
 /// False for case sensitive lookup, true for case insensitive lookup of the static method name.
 /// </param>
 /// <param name="paramTypes">
 /// The types of the parameters (can be null or empty).
 /// </param>
 /// <param name="paramValues">
 /// The values of the parameters (can be null or empty).
 /// </param>
 /// <param name="methodCalls">
 /// The method calls definitions to be made on the object after it is create (can be null).
 /// </param>
 /// <param name="instantiationLifetime">
 /// InstantiationLifetime value stating the length of the lifetime of this instance.
 /// </param>
 ///
 /// <exception cref="ArgumentNullException">
 /// If typeName is null or an array element (besides an element in paramValues corresponding
 /// to a paramType of "null") is null.
 /// </exception>
 /// <exception cref="ArgumentException">
 /// If the paramTypes and paramValues arrays don't have the same size and aren't both null or
 /// if paramTypes has invalid values (see ParamTypes), or assembly is the empty string
 /// or instantiationLifetime is invalid.
 /// </exception>
 public ObjectDefinition(string appDomain, string assembly, string typeName,
     bool ignoreCase, string[] paramTypes, object[] paramValues, MethodCallDefinition[] methodCalls,
     InstantiationLifetime instantiationLifetime)
     : this(appDomain, assembly, typeName, null, ignoreCase,
     instantiationLifetime, paramTypes, paramValues, methodCalls)
 {
     isStatic = false;
 }
 /// <summary>
 /// <p>Creates a new instance of ObjectDefinition.</p>
 /// </summary>
 ///
 /// <param name="appDomain">The application domain (can be null or empty).</param>
 /// <param name="assembly">The assembly name (can be null if appDomain is null or empty).</param>
 /// <param name="typeName">The type name (cannot be null).</param>
 /// <param name="ignoreCase">
 /// False for case sensitive lookup, true for case insensitive lookup of the static method name.
 /// </param>
 /// <param name="paramTypes">
 /// The types of the parameters (can be null or empty).
 /// </param>
 /// <param name="paramValues">
 /// The values of the parameters (can be null or empty).
 /// </param>
 /// <param name="methodCalls">
 /// The method calls definitions to be made on the object after it is create (can be null).
 /// </param>
 /// <param name="instantiationLifetime">
 /// InstantiationLifetime value stating the length of the lifetime of this instance.
 /// </param>
 /// <param name="methodName">The name of the static method to use to create the object.</param>
 ///
 /// <exception cref="ArgumentNullException">
 /// If typeName or methodName is null or an array element (besides an element in paramValues
 /// corresponding to a paramType of "null") is null.
 /// </exception>
 /// <exception cref="ArgumentException">
 /// If the paramTypes and paramValues arrays don't have the same size and aren't both null or
 /// if paramTypes has invalid values (see ParamTypes), or assembly is the empty string
 /// or instantiationLifetime is invalid or methodName is empty.
 /// </exception>
 public ObjectDefinition(string appDomain, string assembly, string typeName, string methodName,
     bool ignoreCase, string[] paramTypes, object[] paramValues, MethodCallDefinition[] methodCalls,
     InstantiationLifetime instantiationLifetime)
     : this(appDomain, assembly, typeName, methodName, ignoreCase, instantiationLifetime,
     paramTypes, paramValues, methodCalls)
 {
     Helper.ValidateNotNull(methodName, "methodName");
     isStatic = true;
 }
        /// <summary>
        /// <p>Applies the given method definition to the given object.</p>
        /// </summary>
        ///
        /// <remarks>
        /// <p>This method will invoke either the method specified by the definition,
        /// or set the property specified by the definition.</p>
        /// </remarks>
        ///
        /// <param name="applyMethodsTo">The object to invoke the method on.</param>
        /// <param name="methodCallDefinition">The definition of the method to invoke.</param>
        ///
        /// <exception cref="MethodInvocationException">If there is an error in invoking the method.</exception>
        /// <exception cref="ArgumentNullException">If any argument is null.</exception>
        public void ApplyMethodCall(object applyMethodsTo, MethodCallDefinition methodCallDefinition)
        {
            Helper.ValidateNotNull(applyMethodsTo, "applyMethodsTo");
            Helper.ValidateNotNull(methodCallDefinition, "methodCallDefinition");

            try
            {
                ApplyMethodCall(applyMethodsTo, methodCallDefinition, new Hashtable(), new Hashtable());
            }
            catch (Exception e)
            {
                throw new MethodInvocationException("Error occurred while invoking the method.", e);
            }
        }
        /// <summary>
        /// <p>Applies the given method definition to the given object.</p>
        /// </summary>
        ///
        /// <remarks>
        /// <p>This method will invoke either the method specified by the definition,
        /// or set the property specified by the definition.</p>
        ///
        /// <p>Exceptions will be promulgated.</p>
        /// </remarks>
        ///
        /// <param name="applyMethodsTo">The object to invoke the method on.</param>
        /// <param name="methodCallDefinition">The definition of the method to invoke.</param>
        /// <param name="objectKeysSeen">
        /// A set store all the keys that are waiting to be solved in the chain of recursive method calls.
        /// </param>
        /// <param name="oncePerTopLevelInstantiationObjects">
        /// A dictionary store the loaded objects that are configured with OncePerTopLevelObject
        /// instantiation lifetime.
        /// </param>
        private void ApplyMethodCall(object applyMethodsTo, MethodCallDefinition methodCallDefinition,
            IDictionary objectKeysSeen, IDictionary oncePerTopLevelInstantiationObjects)
        {
            object[] parameters = GenerateActualParams(
                methodCallDefinition, objectKeysSeen, oncePerTopLevelInstantiationObjects);

            BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
            if (methodCallDefinition.IgnoreCase)
            {
                flags |= BindingFlags.IgnoreCase;
            }

            MethodBase[] methods;
            if (methodCallDefinition.IsProperty)
            {
                // since CLS allow indexed property, more than one properties may have the same name,
                PropertyInfo[] properties = applyMethodsTo.GetType().GetProperties(flags);

                // filter properties by name
                ArrayList candidates = new ArrayList();
                foreach (PropertyInfo candidate in properties)
                {
                    if (String.Compare(candidate.Name, methodCallDefinition.MethodName,
                        methodCallDefinition.IgnoreCase) == 0)
                    {
                        MethodBase setter = candidate.GetSetMethod();

                        if (setter != null)
                        {
                            candidates.Add(setter);
                        }
                    }
                }
                methods = (MethodBase[])candidates.ToArray(typeof(MethodBase));
            }
            else
            {
                // Get a list of methods matched the MethodName
                methods = FilterMethods(applyMethodsTo.GetType(), methodCallDefinition.MethodName, flags);
            }

            // select a method match the given parameters
            object state = null;
            object[] args = parameters;
            MethodBase method = Type.DefaultBinder.BindToMethod(
                flags, methods, ref args, null, null, null, out state);

            // invoke by the given array to insure ref/out parameters work correctly
            method.Invoke(applyMethodsTo, parameters);
        }
        /// <summary>
        /// <para>
        /// Retrieves the object definition from given configuration.
        /// </para>
        /// </summary>
        ///
        /// <param name="config">
        /// The configuration to read.
        /// </param>
        ///
        /// <returns>
        /// The object definition created.
        /// </returns>
        ///
        /// <exception cref="ConfigurationAPISourceException">
        /// If exceptions occur while retrieving the definition info, but may also indicate
        /// incomplete definition info (missing type name), parsing errors if the source
        /// uses string representations or some other errors.
        /// </exception>
        private static ObjectDefinition CreateObjectDefinition(IConfiguration config)
        {
            // Read the app_domain property (optional)
            string appDomain = GetSimpleValueFromChild(config, ConfigAppDomain, false);

            // Read the assembly property (optional)
            string assembly = GetSimpleValueFromChild(config, ConfigAssembly, false);

            // Read the type_name property (required)
            string typeName = GetSimpleValueFromChild(config, ConfigTypeName, true);

            // Read the method_name property (optional)
            string methodName = GetSimpleValueFromChild(config, ConfigMethodName, false);

            // Read the ignore_case property (optional), default to false
            // must be a valid boolean value if exists
            bool ignoreCase = GetBooleanFromChild(config, ConfigIgnoreCase);

            // Read the instantiation_lifetime property (optional), default to Instance
            InstantiationLifetime instantiationLifetime = InstantiationLifetime.Instance;
            string instantiationLifetimeVal = GetSimpleValueFromChild(config, ConfigInstantiationLifetime, false);
            if (instantiationLifetimeVal != null)
            {
                instantiationLifetime = (InstantiationLifetime)Enum.Parse(
                    typeof(InstantiationLifetime), instantiationLifetimeVal, true);
            }

            // Retrieve all values of the parameters property
            string[] paramTypes = null;
            object[] paramValues = null;
            GetParameters(config[ConfigParameters], ref paramTypes, ref paramValues);

            IConfiguration methodsConfig = config[ConfigMethods];

            // Retrieve method definitions
            MethodCallDefinition[] methodCalls = null;
            if (methodsConfig != null)
            {
                int index = 0;
                IConfiguration[] methodConfigs = methodsConfig.Children;
                methodCalls = new MethodCallDefinition[methodConfigs.Length];
                foreach (IConfiguration methodConfig in methodConfigs)
                {
                    methodCalls[index++] = GetMethodDefinition(methodConfig);
                }
            }

            // construct and return an ObjectDefinition instance containing the object infomation
            return methodName == null ?
                new ObjectDefinition(appDomain, assembly, typeName, ignoreCase, paramTypes,
                    paramValues, methodCalls, instantiationLifetime) :
                new ObjectDefinition(appDomain, assembly, typeName, methodName, ignoreCase, paramTypes,
                    paramValues, methodCalls, instantiationLifetime);
        }
        /// <summary>
        /// <para>
        /// Saves the given method definition in NESTED hierarchical structure.
        /// </para>
        /// </summary>
        ///
        /// <param name="name">
        /// The method unique name.
        /// </param>
        /// <param name="definition">
        /// The method definition to be saved.
        /// </param>
        /// <param name="parent">
        /// The methods configuration.
        /// </param>
        private void SaveNestedMethodDefinition(IConfiguration parent, string name, MethodCallDefinition definition)
        {
            // The config of the definition
            IConfiguration methodConfig = new DefaultConfiguration(name);

            // Write all simple attributes of the definition
            AddNewNestedConfig(methodConfig, ConfigIgnoreCase, definition.IgnoreCase);
            AddNewNestedConfig(methodConfig, ConfigMethodName, definition.MethodName);
            AddNewNestedConfig(methodConfig, ConfigIsProperty, definition.IsProperty);

            if (definition.ParamTypes.Length > 0)
            {
                // Write the parameter definitions of the method
                SaveNestedParameters(methodConfig, definition.ParamTypes, definition.ParamValues);
            }

            // Add the new method configuration into methods config
            parent.AddChild(AbstractConfiguration.Synchronized(methodConfig));
        }
        /// <summary>
        /// <para>
        /// Saves the method definition into FLAT structure.
        /// </para>
        /// </summary>
        ///
        /// <param name="nameSpace">
        /// The namespace of the configuration.
        /// </param>
        /// <param name="definition">
        /// The method definition to be saved.
        /// </param>
        private void SaveFlatMethodDefinition(string nameSpace, MethodCallDefinition definition)
        {
            IConfiguration methodConfig = AddRootConfig(ConfigNamespace, nameSpace);
            int propertyIndex = 0;

            // Write all attributes of the method definition
            AddNewFlatConfig(methodConfig, ++propertyIndex, ConfigIgnoreCase, definition.IgnoreCase);
            AddNewFlatConfig(methodConfig, ++propertyIndex, ConfigMethodName, definition.MethodName);
            AddNewFlatConfig(methodConfig, ++propertyIndex, ConfigIsProperty, definition.IsProperty);

            if (definition.ParamTypes.Length > 0)
            {
                // Write parameter definitions
                SaveFlatParameters(
                    nameSpace + "." + ConfigParameters, definition.ParamTypes, definition.ParamValues);
            }
        }