#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;
using System.Collections.Specialized;
using System.Globalization;
using System.Reflection;
using System.Runtime.Remoting;
using Common.Logging;
using Spring.Collections;
using Spring.Core;
using Spring.Core.TypeConversion;
using Spring.Core.TypeResolution;
using Spring.Expressions;
using Spring.Objects;
using Spring.Objects.Factory.Config;
using Spring.Util;

#endregion

namespace Spring.Objects.Factory.Support
{
    /// <summary>
    /// Abstract <see cref="Spring.Objects.Factory.IObjectFactory"/> superclass
    /// that implements default object creation.
    /// </summary>
    /// <remarks>
    /// <p>
    /// Provides object creation, initialization and wiring, supporting
    /// autowiring and constructor resolution. Handles runtime object
    /// references, managed collections, and object destruction.
    /// </p>
    /// <p>
    /// The main template method to be implemented by subclasses is
    /// <see cref="Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory.FindMatchingObjects"/>,
    /// used for autowiring by type. Note that this class does not implement object
    /// definition registry capabilities
    /// (<see cref="Spring.Objects.Factory.Support.DefaultListableObjectFactory"/>
    /// does).
    /// </p>
    /// </remarks>
    /// <author>Rod Johnson</author>
    /// <author>Juergen Hoeller</author>
    /// <author>Rick Evans (.NET)</author>
    [Serializable]
    public abstract class AbstractAutowireCapableObjectFactory : AbstractObjectFactory, IAutowireCapableObjectFactory
    {
        #region Constants

        /// <summary>
        /// The <see cref="System.Reflection.BindingFlags"/> used during the invocation and
        /// searching for of methods.
        /// </summary>
        protected const BindingFlags MethodResolutionFlags =
                BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase;

        #endregion

        /// <summary>
        /// The <see cref="Common.Logging.ILog"/> instance for this class.
        /// </summary>
        private readonly ILog log;

        #region Constructor (s) / Destructor

        /// <summary>
        /// Creates a new instance of the
        /// <see cref="Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory"/>
        /// class.
        /// </summary>
        /// <remarks>
        /// <p>
        /// This is an <see langword="abstract"/> class, and as such exposes no public constructors.
        /// </p>
        /// </remarks>
        /// <param name="caseSensitive">Flag specifying whether to make this object factory case sensitive or not.</param>
        protected AbstractAutowireCapableObjectFactory(bool caseSensitive)
            : this(caseSensitive, null)
        { }

        /// <summary>
        /// Creates a new instance of the
        /// <see cref="Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory"/>
        /// class.
        /// </summary>
        /// <remarks>
        /// <p>
        /// This is an <see langword="abstract"/> class, and as such exposes no public constructors.
        /// </p>
        /// </remarks>
        /// <param name="caseSensitive">Flag specifying whether to make this object factory case sensitive or not.</param>
        /// <param name="parentFactory">The parent object factory, or <see langword="null"/> if none.</param>
        protected AbstractAutowireCapableObjectFactory(bool caseSensitive, IObjectFactory parentFactory)
            : base(caseSensitive, parentFactory)
        {
            log = LogManager.GetLogger(this.GetType());

            this.IgnoreDependencyInterface(typeof(IObjectFactoryAware));
            this.IgnoreDependencyInterface(typeof(IObjectNameAware));
        }

        #endregion

        #region Properties

        /// <summary>
        /// The <see cref="Spring.Objects.Factory.Support.IInstantiationStrategy"/>
        /// implementation to be used to instantiate managed objects.
        /// </summary>
        protected IInstantiationStrategy InstantiationStrategy
        {
            get { return instantiationStrategy; }
            set { instantiationStrategy = value; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Predict the eventual object type (of the processed object instance) for the
        /// specified object.
        /// </summary>
        /// <param name="objectName">Name of the object.</param>
        /// <param name="mod">The merged object definition to determine the type for.</param>
        /// <returns>
        /// The type of the object, or <code>null</code> if not predictable
        /// </returns>
        protected override Type PredictObjectType(string objectName, RootObjectDefinition mod)
        {
            Type objectType;
            if (StringUtils.HasText(mod.FactoryMethodName))
            {
                objectType = GetTypeForFactoryMethod(objectName, mod);
            }
            else
            {
                objectType = ResolveObjectType(mod, objectName);
            }
            return objectType;
        }

        /// <summary>
        /// Determines the <see cref="System.Type"/> of the object defined
        /// by the supplied object <paramref name="definition"/>. 
        /// </summary>
        /// <param name="objectName">
        /// The name associated with the supplied object <paramref name="definition"/>.
        /// </param>
        /// <param name="definition">
        /// The <see cref="Spring.Objects.Factory.Support.RootObjectDefinition"/>
        /// that the <see cref="System.Type"/> is to be determined for. 
        /// </param>
        /// <returns>
        /// The <see cref="System.Type"/> of the object defined by the supplied
        /// object <paramref name="definition"/>; or <see lang="null"/> if the
        /// <see cref="System.Type"/> cannot be determined.
        /// </returns>
        protected override Type GetTypeForFactoryMethod(string objectName, RootObjectDefinition definition)
        {
            Type factoryType = null;
            bool isStatic = true;

            if (StringUtils.HasText(definition.FactoryObjectName))
            {
                // check declared factory method return type on factory type...
                factoryType = GetType(definition.FactoryObjectName);
                isStatic = false;
            }
            else
            {
                factoryType = ResolveObjectType(definition, objectName);
            }
            if (factoryType == null)
            {
                return null;
            }

            // If all factory methods have the same return type, return that type.
            // Can't clearly figure out exact method due to type converting / autowiring!
            int minNrOfArgs = definition.ConstructorArgumentValues.GenericArgumentValues.Count;
            MethodInfo[] candidates = factoryType.GetMethods();
            ISet returnTypes = new HybridSet();
            foreach (MethodInfo factoryMethod in candidates)
            {
#if NET_2_0
                GenericArgumentsHolder genericArgsInfo = new GenericArgumentsHolder(definition.FactoryMethodName);
                if (factoryMethod.IsStatic == isStatic && factoryMethod.Name.Equals(genericArgsInfo.GenericMethodName)
                    && ReflectionUtils.GetParameterTypes(factoryMethod).Length >= minNrOfArgs
                    && factoryMethod.GetGenericArguments().Length == genericArgsInfo.GetGenericArguments().Length)
                {
                    if (genericArgsInfo.ContainsGenericArguments)
                    {
                        string[] unresolvedGenericArgs = genericArgsInfo.GetGenericArguments();
                        Type[] genericArgs = new Type[unresolvedGenericArgs.Length];
                        for (int j = 0; j < unresolvedGenericArgs.Length; j++)
                        {
                            genericArgs[j] = TypeResolutionUtils.ResolveType(unresolvedGenericArgs[j]);
                        }
                        returnTypes.Add(factoryMethod.MakeGenericMethod(genericArgs).ReturnType);
                    }
                    else
                    {
                        returnTypes.Add(factoryMethod.ReturnType);
                    }
                }
#else
                if (factoryMethod.IsStatic == isStatic && factoryMethod.Name.Equals(definition.FactoryMethodName)
                    && ReflectionUtils.GetParameterTypes(factoryMethod).Length >= minNrOfArgs)
                {
                    returnTypes.Add(factoryMethod.ReturnType);
                }
#endif
            }
            if (returnTypes.Count == 1)
            {
                // clear return type found: all factory methods return same type...
                return (Type)ObjectUtils.EnumerateFirstElement(returnTypes);
            }

            // ambiguous return types found: return null to indicate "not determinable"...
            return null;
        }

        /// <summary>
        /// Apply the property values of the object definition with the supplied
        /// <paramref name="name"/> to the supplied <paramref name="instance"/>.
        /// </summary>
        /// <param name="instance">
        /// The existing object that the property values for the named object will
        /// be applied to.
        /// </param>
        /// <param name="name">
        /// The name of the object definition associated with the property values that are
        /// to be applied.
        /// </param>
        public override void ApplyObjectPropertyValues(object instance, string name)
        {
            RootObjectDefinition definition = GetMergedObjectDefinition(name, true);
            if (definition != null)
            {
                log.Debug(string.Format("configuring object '{0}' using definition '{1}'", instance, name));
                ApplyPropertyValues(name, definition, new ObjectWrapper(instance), definition.PropertyValues);
            }
        }

        /// <summary>
        /// Apply any
        /// <see cref="Spring.Objects.Factory.Config.IInstantiationAwareObjectPostProcessor"/>s.
        /// </summary>
        /// <remarks>
        /// <p>
        /// The returned instance may be a wrapper around the original.
        /// </p>
        /// </remarks>
        /// <param name="objectType">
        /// The <see cref="System.Type"/> of the object that is to be
        /// instantiated.
        /// </param>
        /// <param name="objectName">
        /// The name of the object that is to be instantiated.
        /// </param>
        /// <returns>
        /// An instance to use in place of the original instance.
        /// </returns>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// In case of errors.
        /// </exception>
        protected object ApplyObjectPostProcessorsBeforeInstantiation(Type objectType, string objectName)
        {
            #region Instrumentation

            if (log.IsDebugEnabled)
            {
                log.Debug(string.Format("Invoking IInstantiationAwareObjectPostProcessors before " + "the instantiation of '{0}'.", objectName));
            }

            #endregion

            foreach (IObjectPostProcessor processor in ObjectPostProcessors)
            {
                IInstantiationAwareObjectPostProcessor inProc = processor as IInstantiationAwareObjectPostProcessor;
                if (inProc != null)
                {
                    object theObject = inProc.PostProcessBeforeInstantiation(objectType, objectName);
                    if (theObject != null)
                    {
                        return theObject;
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// Apply the given property values, resolving any runtime references
        /// to other objects in this object factory.
        /// </summary>
        /// <param name="name">
        /// The object name passed for better exception information.
        /// </param>
        /// <param name="definition">
        /// The definition of the named object.
        /// </param>
        /// <param name="wrapper">
        /// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object.
        /// </param>
        /// <param name="properties">
        /// The new property values.
        /// </param>
        /// <remarks>
        /// <p>
        /// Must use deep copy, so that we don't permanently modify this property.
        /// </p>
        /// </remarks>
        protected void ApplyPropertyValues(string name, RootObjectDefinition definition, IObjectWrapper wrapper, IPropertyValues properties)
        {
            if (properties == null || properties.PropertyValues.Length == 0)
            {
                return;
            }
            ObjectDefinitionValueResolver valueResolver = CreateValueResolver();

            MutablePropertyValues deepCopy = new MutablePropertyValues(properties);
            PropertyValue[] copiedProperties = deepCopy.PropertyValues;
            for (int i = 0; i < copiedProperties.Length; ++i)
            {
                PropertyValue copiedProperty = copiedProperties[i];
                //(string name, RootObjectDefinition definition, string argumentName, object argumentValue)
                object value = valueResolver.ResolveValueIfNecessary(name, definition, copiedProperty.Name, copiedProperty.Value);
                // object value = ResolveValueIfNecessary(name, definition, copiedProperty.Name, copiedProperty.Value);
                PropertyValue propertyValue = new PropertyValue(copiedProperty.Name, value, copiedProperty.Expression);
                // update mutable copy...
                deepCopy.SetPropertyValueAt(propertyValue, i);
            }
            // set the (possibly resolved) deep copy properties...
            try
            {
                wrapper.SetPropertyValues(deepCopy);
            }
            catch (ObjectsException ex)
            {
                // improve the message by showing the context...
                throw new ObjectCreationException(definition.ResourceDescription, name, "Error setting property values: " + ex.Message, ex);
            }
        }

        /// <summary>
        /// Create the value resolver strategy to use for resolving raw property values
        /// </summary>
        protected virtual ObjectDefinitionValueResolver CreateValueResolver()
        {
            return new ObjectDefinitionValueResolver(this);
        }

        /// <summary>
        /// Return an array of object-type property names that are unsatisfied.
        /// </summary>
        /// <remarks>
        /// <p>
        /// These are probably unsatisfied references to other objects in the
        /// factory. Does not include simple properties like primitives or
        /// <see cref="System.String"/>s.
        /// </p>
        /// </remarks>
        /// <returns>
        /// An array of object-type property names that are unsatisfied.
        /// </returns>
        /// <param name="definition">
        /// The definition of the named object.
        /// </param>
        /// <param name="wrapper">
        /// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object.
        /// </param>
        protected string[] UnsatisfiedNonSimpleProperties(RootObjectDefinition definition, IObjectWrapper wrapper)
        {
            ListSet results = new ListSet();
            IPropertyValues pvs = definition.PropertyValues;
            PropertyInfo[] properties = wrapper.GetPropertyInfos();
            foreach (PropertyInfo property in properties)
            {
                string name = property.Name;
                if (property.CanWrite
                    && !IsExcludedFromDependencyCheck(property)
                    && !pvs.Contains(name)
                    && !ObjectUtils.IsSimpleProperty(property.PropertyType))
                {
                    results.Add(name);
                }
            }
            return (string[])CollectionUtils.ToArray(results, typeof(string));
        }

        /// <summary>
        /// Destroy all cached singletons in this factory.
        /// </summary>
        /// <remarks>
        /// <p>
        /// To be called on shutdown of a factory.
        /// </p>
        /// </remarks>
        public override void Dispose()
        {
            base.Dispose();
            foreach (object o in DisposableInnerObjects)
            {
                DestroyObject(string.Format(CultureInfo.InvariantCulture, "(Inner object of Type '{0}')", o.GetType().FullName), o);
            }
            DisposableInnerObjects.Clear();
        }

        /// <summary>
        /// Populate the object instance in the given
        /// <see cref="Spring.Objects.IObjectWrapper"/> with the property values from the
        /// object definition.
        /// </summary>
        /// <param name="name">
        /// The name of the object.
        /// </param>
        /// <param name="definition">
        /// The definition of the named object.
        /// </param>
        /// <param name="wrapper">
        /// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object.
        /// </param>
        protected void PopulateObject(string name, RootObjectDefinition definition, IObjectWrapper wrapper)
        {
            // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
            // state of the bean before properties are set. This can be used, for example,
            // to support styles of field injection.
            bool continueWithPropertyPopulation = true;

            if (HasInstantiationAwareBeanPostProcessors)
            {
                foreach (IObjectPostProcessor processor in ObjectPostProcessors)
                {
                    IInstantiationAwareObjectPostProcessor inProc = processor as IInstantiationAwareObjectPostProcessor;
                    if (inProc != null)
                    {
                        if (!inProc.PostProcessAfterInstantiation(wrapper.WrappedInstance, name))
                        {
                            continueWithPropertyPopulation = false;
                            break;
                        }
                    }
                }
            }
            if (!continueWithPropertyPopulation)
            {
                return;
            }

            IPropertyValues properties = definition.PropertyValues;

            if (wrapper == null)
            {
                if (properties.PropertyValues.Length > 0)
                {
                    throw new ObjectCreationException(definition.ResourceDescription,
                        name, "Cannot apply property values to null instance.");
                }

                // skip property population phase for null instance
                return;
            }

            if (definition.ResolvedAutowireMode == AutoWiringMode.ByName || definition.ResolvedAutowireMode == AutoWiringMode.ByType)
            {
                MutablePropertyValues mpvs = new MutablePropertyValues(properties);
                // add property values based on autowire by name if it's applied
                if (definition.ResolvedAutowireMode == AutoWiringMode.ByName)
                {
                    AutowireByName(name, definition, wrapper, mpvs);
                }
                // add property values based on autowire by type if it's applied
                if (definition.ResolvedAutowireMode == AutoWiringMode.ByType)
                {
                    AutowireByType(name, definition, wrapper, mpvs);
                }
                properties = mpvs;
            }
            //DependencyCheck(name, definition, wrapper, properties);


            bool hasInstAwareOpps = HasInstantiationAwareBeanPostProcessors;
            bool needsDepCheck = (definition.DependencyCheck != DependencyCheckingMode.None);


            if (hasInstAwareOpps || needsDepCheck)
            {
                PropertyInfo[] filteredPropInfo = FilterPropertyInfoForDependencyCheck(wrapper);
                if (hasInstAwareOpps)
                {
                    foreach (IObjectPostProcessor processor in ObjectPostProcessors)
                    {
                        IInstantiationAwareObjectPostProcessor instantiationAwareObjectPostProcessor =
                            processor as IInstantiationAwareObjectPostProcessor;
                        if (instantiationAwareObjectPostProcessor != null)
                        {
                            properties =
                                instantiationAwareObjectPostProcessor.PostProcessPropertyValues(properties, filteredPropInfo, wrapper.WrappedInstance,
                                                                 name);
                            if (properties == null)
                            {
                                return;
                            }
                        }
                    }
                }

                if (needsDepCheck)
                {
                    CheckDependencies(name, definition, filteredPropInfo, properties);
                }

            }

            ApplyPropertyValues(name, definition, wrapper, properties);
        }

        /// <summary>
        /// Wires up any exposed events in the object instance in the given
        /// <see cref="Spring.Objects.IObjectWrapper"/> with any event handler
        /// values from the <paramref name="definition"/>.
        /// </summary>
        /// <param name="name">
        /// The name of the object.
        /// </param>
        /// <param name="definition">
        /// The definition of the named object.
        /// </param>
        /// <param name="wrapper">
        /// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object.
        /// </param>
        protected void WireEvents(string name, IConfigurableObjectDefinition definition, IObjectWrapper wrapper)
        {
            foreach (string eventName in definition.EventHandlerValues.Events)
            {
                foreach (IEventHandlerValue handlerValue
                        in definition.EventHandlerValues[eventName])
                {
                    object handler = null;
                    if (handlerValue.Source is RuntimeObjectReference)
                    {
                        RuntimeObjectReference roref = (RuntimeObjectReference)handlerValue.Source;
                        handler = ResolveReference(definition, name, eventName, roref);
                    }
                    else if (handlerValue.Source is Type)
                    {
                        // a static Type event is being wired up; simply pass on the Type
                        handler = handlerValue.Source;
                    }
                    else if (handlerValue.Source is string)
                    {
                        // a static Type event is being wired up; we need to resolve the Type
                        handler = TypeResolutionUtils.ResolveType(handlerValue.Source as string);
                    }
                    else
                    {
                        throw new FatalObjectException("Currently, only references to other objects and Types are " + "supported as event sources.");
                    }
                    handlerValue.Wire(handler, wrapper.WrappedInstance);
                }
            }
        }

        /// <summary>
        /// Fills in any missing property values with references to
        /// other objects in this factory if autowire is set to
        /// <see cref="Spring.Objects.Factory.Config.AutoWiringMode.ByName"/>.
        /// </summary>
        /// <param name="name">
        /// The object name to be autowired by <see cref="System.Type"/>.
        /// </param>
        /// <param name="definition">
        /// The definition of the named object to update through autowiring.
        /// </param>
        /// <param name="wrapper">
        /// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object (and
        /// from which we can rip out information concerning the object).
        /// </param>
        /// <param name="properties">
        /// The property values to register wired objects with.
        /// </param>
        protected void AutowireByName(string name, RootObjectDefinition definition, IObjectWrapper wrapper, MutablePropertyValues properties)
        {
            string[] propertyNames = UnsatisfiedNonSimpleProperties(definition, wrapper);
            foreach (string propertyName in propertyNames)
            {
                // look for a matching type
                if (ContainsObject(propertyName))
                {
                    object o = GetObject(propertyName);
                    properties.Add(propertyName, o);

                    #region Instrumentation

                    if (log.IsDebugEnabled)
                    {
                        log.Debug(
                                string.Format(CultureInfo.InvariantCulture,
                                              "Added autowiring by name from object name '{0}' via " + "property '{1}' to object named '{1}'.", name,
                                              propertyName));
                    }

                    #endregion
                }
                else
                {
                    #region Instrumentation

                    if (log.IsDebugEnabled)
                    {
                        log.Debug(
                                string.Format(CultureInfo.InvariantCulture,
                                              "Not autowiring property '{0}' of object '{1}' by name: " + "no matching object found.", propertyName, name));
                    }

                    #endregion
                }
            }
        }

        /// <summary>
        /// Defines "autowire by type" (object properties by type) behavior.
        /// </summary>
        /// <remarks>
        /// <p>
        /// This is like PicoContainer default, in which there must be exactly one object
        /// of the property type in the object factory. This makes object factories simple
        /// to configure for small namespaces, but doesn't work as well as standard Spring
        /// behavior for bigger applications.
        /// </p>
        /// </remarks>
        /// <param name="name">
        /// The object name to be autowired by <see cref="System.Type"/>.
        /// </param>
        /// <param name="definition">
        /// The definition of the named object to update through autowiring.
        /// </param>
        /// <param name="wrapper">
        /// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object (and
        /// from which we can rip out information concerning the object).
        /// </param>
        /// <param name="properties">
        /// The property values to register wired objects with.
        /// </param>
        protected void AutowireByType(string name, RootObjectDefinition definition, IObjectWrapper wrapper, MutablePropertyValues properties)
        {
            string[] propertyNames = UnsatisfiedNonSimpleProperties(definition, wrapper);
            foreach (string propertyName in propertyNames)
            {
                // look for a matching type
                Type requiredType = wrapper.GetPropertyType(propertyName);
                IDictionary matchingObjects = FindMatchingObjects(requiredType);
                if (matchingObjects != null && matchingObjects.Count == 1)
                {
                    properties.Add(propertyName, ObjectUtils.EnumerateFirstElement(matchingObjects.Values));

                    #region Instrumentation

                    if (log.IsDebugEnabled)
                    {
                        log.Debug(
                                string.Format(CultureInfo.InvariantCulture,
                                              "Autowiring by type from object name '{0}' via property " + "'{1}' to object named '{2}'.", name,
                                              propertyName, ObjectUtils.EnumerateFirstElement(matchingObjects.Keys)));
                    }

                    #endregion
                }
                else if (matchingObjects != null && matchingObjects.Count > 1)
                {
                    throw new UnsatisfiedDependencyException(string.Empty, name, propertyName,
                                                             string.Format(CultureInfo.InvariantCulture,
                                                                           "There are {0} objects of Type [{1}] for autowire by "
                                                                           + "type, when there should have been just 1 to be able to "
                                                                           + "autowire property '{2}' of object '{3}'.", matchingObjects.Count,
                                                                           requiredType, propertyName, name));
                }
                else
                {
                    #region Instrumentation

                    if (log.IsDebugEnabled)
                    {
                        log.Debug(
                                string.Format(CultureInfo.InvariantCulture, "Not autowiring property '{0}' of object '{1}': no matching object found.",
                                              propertyName, name));
                    }

                    #endregion
                }
            }
        }

        /// <summary>
        /// Ignore the given dependency type for autowiring
        /// </summary>
        /// <remarks>
        /// This will typically be used by application contexts to register
        /// dependencies that are resolved in other ways, like IOjbectFactory through
        /// IObjectFactoryAware or IApplicationContext through IApplicationContextAware.
        /// By default, IObjectFactoryAware and IObjectName interfaces are ignored.
        /// For further types to ignore, invoke this method for each type.
        /// </remarks>
        /// <seealso cref="Spring.Objects.Factory.Config.IConfigurableObjectFactory.IgnoreDependencyType"/>.
        public void IgnoreDependencyInterface(Type type)
        {
            ignoredDependencyInterfaces.Add(type);
        }

//        /// <summary>
//        /// Create an object instance for the given object definition.
//        /// </summary>
//        /// <param name="name">The name of the object.</param>
//        /// <param name="definition">
//        /// The object definition for the object that is to be instantiated.
//        /// </param>
//        /// <param name="arguments">
//        /// The arguments to use if creating a prototype using explicit arguments to
//        /// a static factory method. It is invalid to use a non-<see langword="null"/> arguments value
//        /// in any other case.
//        /// </param>
//        /// <returns>
//        /// A new instance of the object.
//        /// </returns>
//        /// <exception cref="Spring.Objects.ObjectsException">
//        /// In case of errors.
//        /// </exception>
//        /// <remarks>
//        /// <p>
//        /// Delegates to the
//        /// <see cref="Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory.CreateObject (string,RootObjectDefinition,object[],bool)"/>
//        /// method version with the <c>allowEagerCaching</c> parameter set to <b>true</b>.
//        /// </p>
//        /// <p>
//        /// The object definition will already have been merged with the parent
//        /// definition in case of a child definition.
//        /// </p>
//        /// <p>
//        /// All the other methods in this class invoke this method, although objects
//        /// may be cached after being instantiated by this method. All object
//        /// instantiation within this class is performed by this method.
//        /// </p>
//        /// </remarks>
//        protected internal override object CreateObject(string name, RootObjectDefinition definition, object[] arguments)
//        {
//            return CreateObject(name, definition, arguments, true, false);
//        }

        /// <summary>
        /// Create an object instance for the given object definition.
        /// </summary>
        /// <param name="name">The name of the object.</param>
        /// <param name="definition">
        /// The object definition for the object that is to be instantiated.
        /// </param>
        /// <param name="arguments">
        /// The arguments to use if creating a prototype using explicit arguments to
        /// a static factory method. It is invalid to use a non-<see langword="null"/> arguments value
        /// in any other case.
        /// </param>
        /// <param name="allowEagerCaching">
        /// Whether eager caching of singletons is allowed... typically true for
        /// singlton objects, but never true for inner object definitions.
        /// </param>
        /// <param name="suppressConfigure">
        /// Suppress injecting dependencies yet.
        /// </param>
        /// <returns>
        /// A new instance of the object.
        /// </returns>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// In case of errors.
        /// </exception>
        /// <remarks>
        /// <p>
        /// The object definition will already have been merged with the parent
        /// definition in case of a child definition.
        /// </p>
        /// <p>
        /// All the other methods in this class invoke this method, although objects
        /// may be cached after being instantiated by this method. All object
        /// instantiation within this class is performed by this method.
        /// </p>
        /// </remarks>
        protected internal override object InstantiateObject(string name, RootObjectDefinition definition, object[] arguments, bool allowEagerCaching, bool suppressConfigure)
        {
            // guarantee the initialization of objects that the current one depends on..
            if (definition.DependsOn != null && definition.DependsOn.Length > 0)
            {
                foreach (string dependant in definition.DependsOn)
                {
                    GetObject(dependant);
                }
            }

            #region Instrumentation

            if (log.IsDebugEnabled)
            {
                log.Debug(string.Format(CultureInfo.InvariantCulture, "Creating instance of Object '{0}' with merged definition [{1}].", name, definition));
            }

            #endregion

            // Make sure object type is actually resolved at this point.
            ResolveObjectType(definition, name);

            try
            {
                definition.PrepareMethodOverrides();
            }
            catch (ObjectDefinitionValidationException ex)
            {
                throw new ObjectDefinitionStoreException(definition.ResourceDescription, name,
                                                         "Validation of method overrides failed.  " + ex.Message, ex);
            }

            // return IObjectDefinition instance itself for an abstract object-definition
            if (definition.IsTemplate)
            {
                return definition;
            }



            object instance = null;


            IObjectWrapper instanceWrapper = null;
            bool eagerlyCached = false;
            try
            {
                // Give IInstantiationAwareObjectPostProcessors a chance to return a proxy instead of the target instance....
                if (definition.HasObjectType)
                {
                    instance = ApplyObjectPostProcessorsBeforeInstantiation(definition.ObjectType, name);
                    if (instance != null)
                    {
                        return instance;
                    }
                }


                instanceWrapper = CreateObjectInstance(name, definition, arguments);
                instance = instanceWrapper.WrappedInstance;

                // eagerly cache singletons to be able to resolve circular references
                // even when triggered by lifecycle interfaces like IObjectFactoryAware.
                if (allowEagerCaching && definition.IsSingleton)
                {
                    if (log.IsDebugEnabled)
                    {
                        log.Debug("Eagerly caching object '" + name + "' to allow for resolving potential circular references");
                    }
                    AddEagerlyCachedSingleton(name, definition, instance);
                    eagerlyCached = true;
                }

                if (!suppressConfigure)
                {
                    instance = ConfigureObject(name, definition, instanceWrapper);
                }
            }
            catch (ObjectCreationException)
            {
                if (eagerlyCached)
                {
                    RemoveEagerlyCachedSingleton(name, definition);
                }
                throw;
            }
            catch (Exception ex)
            {
                if (eagerlyCached)
                {
                    RemoveEagerlyCachedSingleton(name, definition);
                }
                throw new ObjectCreationException(definition.ResourceDescription, name, "Initialization of object failed : " + ex.Message, ex);
            }
            return instance;
        }

        /// <summary>
        /// Add the created, but yet unpopulated singleton to the singleton cache
        /// to be able to resolve circular references
        /// </summary>
        /// <param name="objectName">the name of the object to add to the cache.</param>
        /// <param name="objectDefinition">the definition used to create and populated the object.</param>
        /// <param name="rawSingletonInstance">the raw object instance.</param>
        /// <remarks>
        /// Derived classes may override this method to select the right cache based on the object definition.
        /// </remarks>
        protected virtual void AddEagerlyCachedSingleton(string objectName, IObjectDefinition objectDefinition, object rawSingletonInstance)
        {
            base.AddSingleton(objectName, rawSingletonInstance);
        }

        /// <summary>
        /// Remove the specified singleton from the singleton cache that has 
        /// been added before by a call to <see cref="AddEagerlyCachedSingleton"/>
        /// </summary>
        /// <param name="objectName">the name of the object to remove from the cache.</param>
        /// <param name="objectDefinition">the definition used to create and populated the object.</param>
        /// <remarks>
        /// Derived classes may override this method to select the right cache based on the object definition.
        /// </remarks>
        protected virtual void RemoveEagerlyCachedSingleton(string objectName, IObjectDefinition objectDefinition)
        {
            base.RemoveSingleton(objectName);
        }

        /// <summary>
        /// Creates an <see cref="IObjectWrapper"/> instance from the <see cref="RootObjectDefinition"/> passed in <paramref name="objectDefinition"/>
        /// using constructor <paramref name="arguments"/>
        /// </summary>
        /// <param name="objectName">The name of the object to create - used for error messages.</param>
        /// <param name="objectDefinition">The <see cref="RootObjectDefinition"/> describing the object to be created.</param>
        /// <param name="arguments">optional arguments to pass to the constructor</param>
        /// <returns>An <see cref="IObjectWrapper"/> wrapping the already instantiated object</returns>
        protected IObjectWrapper CreateObjectInstance(string objectName, RootObjectDefinition objectDefinition, object[] arguments)
        {
            // Make sure object class is actually resolved at this point.
            Type objectType = ResolveObjectType(objectDefinition, objectName);
            if (StringUtils.HasText(objectDefinition.FactoryMethodName))
            {
                return InstantiateUsingFactoryMethod(objectName, objectDefinition, arguments);
            }

            //TODO perf optimization when creating the same object

            ConstructorInfo[] ctors = DetermineConstructorsFromObjectPostProcessors(objectType, objectName);
            if (ctors != null ||
                objectDefinition.ResolvedAutowireMode == AutoWiringMode.Constructor ||
                objectDefinition.HasConstructorArgumentValues || !ObjectUtils.IsEmpty(arguments))
            {
                return AutowireConstructor(objectName, objectDefinition, ctors, arguments);
            }

            // No special handling: simply use no-arg constructor.
            return InstantiateObject(objectName, objectDefinition);

            /*
            IObjectWrapper instanceWrapper;
            if (StringUtils.HasText(definition.FactoryMethodName))
            {
                instanceWrapper = InstantiateUsingFactoryMethod(name, definition, arguments);
            }
            //Handle case when arguments are passed in explicitly.
            else if (arguments != null && arguments.Length > 0)
            {
                instanceWrapper = AutowireConstructor(name, definition, arguments);
            }
            else if (definition.ResolvedAutowireMode == AutoWiringMode.Constructor ||
                     definition.HasConstructorArgumentValues)
            {
                instanceWrapper = AutowireConstructor(name, definition);
            }
            else
            {
                instanceWrapper = new ObjectWrapper(InstantiationStrategy.Instantiate(definition, name, this));
                InitObjectWrapper(instanceWrapper);
            }
            return instanceWrapper;
            
            */
        }

        /// <summary>
        /// Instantiates the given object using its default constructor
        /// </summary>
        /// <param name="objectName">Name of the object.</param>
        /// <param name="definition">The definition.</param>
        /// <returns>IObjectWrapper for the new instance</returns>
        protected virtual IObjectWrapper InstantiateObject(string objectName, RootObjectDefinition definition)
        {
            return new ObjectWrapper(InstantiationStrategy.Instantiate(definition, objectName, this));
        }

        /// <summary>
        /// Determines candidate constructors to use for the given bean, checking all registered
        /// <see cref="SmartInstantiationAwareObjectPostProcessor"/>
        /// </summary>
        /// <param name="objectType">Raw type of the object.</param>
        /// <param name="objectName">Name of the object.</param>
        /// <returns>the candidate constructors, or <code>null</code> if none specified</returns>
        /// <exception cref="ObjectsException">In case of errors</exception>
        /// <seealso cref="SmartInstantiationAwareObjectPostProcessor.DetermineCandidateConstructors"/>
        protected virtual ConstructorInfo[] DetermineConstructorsFromObjectPostProcessors(Type objectType, string objectName)
        {
            if (HasInstantiationAwareBeanPostProcessors)
            {
                foreach (IObjectPostProcessor objectPostProcessor in ObjectPostProcessors)
                {
                    if (ObjectUtils.IsAssignable(typeof(SmartInstantiationAwareObjectPostProcessor), objectPostProcessor))
                    {
                        SmartInstantiationAwareObjectPostProcessor iop =
                            (SmartInstantiationAwareObjectPostProcessor)objectPostProcessor;
                        ConstructorInfo[] ctors = iop.DetermineCandidateConstructors(objectType, objectName);
                        if (ctors != null)
                        {
                            return ctors;
                        }
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// Instantiate an object instance using a named factory method.
        /// </summary>
        /// <remarks>
        /// <p>
        /// The method may be static, if the <paramref name="definition"/>
        /// parameter specifies a class, rather than a
        /// <see cref="Spring.Objects.Factory.IFactoryObject"/> instance, or an
        /// instance variable on a factory object itself configured using Dependency
        /// Injection.
        /// </p>
        /// <p>
        /// Implementation requires iterating over the static or instance methods
        /// with the name specified in the supplied <paramref name="definition"/>
        /// (the method may be overloaded) and trying to match with the parameters.
        /// We don't have the types attached to constructor args, so trial and error
        /// is the only way to go here.
        /// </p>
        /// </remarks>
        /// <param name="name">
        /// The name associated with the supplied <paramref name="definition"/>.
        /// </param>
        /// <param name="definition">
        /// The definition describing the instance that is to be instantiated.
        /// </param>
        /// <param name="arguments">
        /// Any arguments to the factory method that is to be invoked.
        /// </param>
        /// <returns>
        /// The result of the factory method invocation (the instance).
        /// </returns>
        protected virtual IObjectWrapper InstantiateUsingFactoryMethod(string name, RootObjectDefinition definition, object[] arguments)
        {
            ConstructorResolver constructorResolver =
                    new ConstructorResolver(this, this, InstantiationStrategy);
            return constructorResolver.InstantiateUsingFactoryMethod(name, definition, arguments);
        }

        /// <summary>
        /// "autowire constructor" (with constructor arguments by type) behaviour.
        /// </summary>
        /// <param name="name">The name of the object to autowire by type.</param>
        /// <param name="definition">The object definition to update through autowiring.</param>
        /// <param name="ctors">The chosen candidate constructors.</param>
        /// <param name="explicitArgs">The argument values passed in programmatically via the GetObject method,
        /// or <code>null</code> if none (-> use constructor argument values from object definition)</param>
        /// <returns>
        /// An <see cref="Spring.Objects.IObjectWrapper"/> for the new instance.
        /// </returns>
        /// <remarks>
        /// <para>
        /// Also applied if explicit constructor argument values are specified,
        /// matching all remaining arguments with objects from the object factory.
        /// </para>
        /// <para>
        /// This corresponds to constructor injection: in this mode, a Spring.NET
        /// object factory is able to host components that expect constructor-based
        /// dependency resolution.
        /// </para>
        /// </remarks>
        protected IObjectWrapper AutowireConstructor(string name, RootObjectDefinition definition, ConstructorInfo[] ctors, object[] explicitArgs)
        {
            ConstructorResolver constructorResolver =
                new ConstructorResolver(this, this, InstantiationStrategy);
            return constructorResolver.AutowireConstructor(name, definition, ctors, explicitArgs);

        }

        /// <summary>
        /// Perform a dependency check that all properties exposed have been set, if desired.
        /// </summary>
        /// <remarks>
        /// <p>
        /// Dependency checks can be objects (collaborating objects), simple (primitives
        /// and <see cref="System.String"/>), or all (both).
        /// </p>
        /// </remarks>
        /// <param name="name">
        /// The name of the object.
        /// </param>
        /// <param name="definition">
        /// The definition of the named object.
        /// </param>
        /// <param name="wrapper">
        /// The <see cref="Spring.Objects.IObjectWrapper"/> wrapping the target object.
        /// </param>
        /// <param name="properties">
        /// The property values to be checked.
        /// </param>
        /// <exception cref="Spring.Objects.Factory.UnsatisfiedDependencyException">
        /// If all of the checked dependencies were not satisfied.
        /// </exception>
        protected void DependencyCheck(string name, IConfigurableObjectDefinition definition, IObjectWrapper wrapper, IPropertyValues properties)
        {
            DependencyCheckingMode dependencyCheck = definition.DependencyCheck;
            if (dependencyCheck == DependencyCheckingMode.None)
            {
                return;
            }

            PropertyInfo[] filteredPropInfo = FilterPropertyInfoForDependencyCheck(wrapper);
            if (HasInstantiationAwareBeanPostProcessors)
            {
                foreach (IObjectPostProcessor processor in ObjectPostProcessors)
                {
                    IInstantiationAwareObjectPostProcessor inProc = processor as IInstantiationAwareObjectPostProcessor;
                    if (inProc != null)
                    {
                        properties =
                            inProc.PostProcessPropertyValues(properties, filteredPropInfo, wrapper.WrappedInstance, name);
                        if (properties == null)
                        {
                            return;
                        }
                    }
                }
            }


            CheckDependencies(name, definition, filteredPropInfo, properties);
        }

        private static void CheckDependencies(string name, IConfigurableObjectDefinition definition, PropertyInfo[] filteredPropInfo, IPropertyValues properties)
        {
            DependencyCheckingMode dependencyCheck = definition.DependencyCheck;
            foreach (PropertyInfo property in filteredPropInfo)
            {
                if (property.CanWrite && properties.GetPropertyValue(property.Name) == null)
                {
                    bool isSimple = ObjectUtils.IsSimpleProperty(property.PropertyType);
                    bool unsatisfied = (dependencyCheck == DependencyCheckingMode.All) || (isSimple && dependencyCheck == DependencyCheckingMode.Simple)
                                       || (!isSimple && dependencyCheck == DependencyCheckingMode.Objects);
                    if (unsatisfied)
                    {
                        throw new UnsatisfiedDependencyException(definition.ResourceDescription, name, property.Name,
                            "Set this property value or disable dependency checking for this object.");
                    }
                }
            }
        }

        /// <summary>
        /// Extract a filtered set of PropertyInfos from the given IObjectWrapper, excluding
        /// ignored dependency types.
        /// </summary>
        /// <param name="wrapper">The object wrapper the object was created with.</param>
        /// <returns>The filtered PropertyInfos</returns>
        private PropertyInfo[] FilterPropertyInfoForDependencyCheck(IObjectWrapper wrapper)
        {
            lock (filteredPropertyDescriptorsCache)
            {
                PropertyInfo[] filtered = (PropertyInfo[])filteredPropertyDescriptorsCache[wrapper.WrappedType];
                if (filtered == null)
                {

                    ArrayList list = new ArrayList(wrapper.GetPropertyInfos());
                    for (int i = list.Count - 1; i >= 0; i--)
                    {
                        PropertyInfo pi = (PropertyInfo)list[i];
                        if (IsExcludedFromDependencyCheck(pi))
                        {
                            list.RemoveAt(i);
                        }
                    }

                    filtered = (PropertyInfo[])list.ToArray(typeof(PropertyInfo));
                    filteredPropertyDescriptorsCache.Add(wrapper.WrappedType, filtered);
                }
                return filtered;
            }

        }

        /// <summary>
        /// Determine whether the given bean property is excluded from dependency checks.
        /// This implementation excludes properties whose type matches an ignored dependency type
        /// or which are defined by an ignored dependency interface.
        /// </summary>
        /// <seealso cref="AbstractObjectFactory.IgnoreDependencyType(Type)"/>
        /// <seealso cref="IgnoreDependencyInterface(Type)"/>
        /// <param name="property">the <see cref="PropertyInfo"/> of the object property</param>
        /// <returns>whether the object property is excluded</returns>
        private bool IsExcludedFromDependencyCheck(PropertyInfo property)
        {
            bool b1 = !property.CanWrite; //AutowireUtils.IsExcludedFromDependencyCheck(pi);
            bool b2 = IgnoredDependencyTypes.Contains(property.PropertyType);
            bool b3 = AutowireUtils.IsSetterDefinedInInterface(property, ignoredDependencyInterfaces);
            return b1 || b2 || b3;
            /*
            return AutowireUtils.IsExcludedFromDependencyCheck(pi) ||
                   IgnoredDependencyTypes.Contains(pi.PropertyType) ||
                   AutowireUtils.IsSetterDefinedInInterface(pi, ignoredDependencyInterfaces);
             */
        }

        /// <summary>
        /// Give an object a chance to react now all its properties are set,
        /// and a chance to know about its owning object factory (this object).
        /// </summary>
        /// <remarks>
        /// <p>
        /// This means checking whether the object implements
        /// <see cref="Spring.Objects.Factory.IInitializingObject"/> and  / or
        /// <see cref="Spring.Objects.Factory.IObjectFactoryAware"/>, and invoking the
        /// necessary callback(s) if it does.
        /// </p>
        /// <p>
        /// Custom init methods are resolved in a <b>case-insensitive</b> manner.
        /// </p>
        /// </remarks>
        /// <param name="target">
        /// The new object instance we may need to initialise.
        /// </param>
        /// <param name="name">
        /// The name the object has in the factory. Used for logging output.
        /// </param>
        /// <param name="definition">
        /// The definition of the target object instance.
        /// </param>
        protected virtual void InvokeInitMethods(object target, string name, IConfigurableObjectDefinition definition)
        {
            if (ObjectUtils.IsAssignableAndNotTransparentProxy(typeof(IInitializingObject), target))
            {
                #region Instrumentation

                if (log.IsDebugEnabled)
                {
                    log.Debug(string.Format(CultureInfo.InvariantCulture, "Calling AfterPropertiesSet() on object with name '{0}'.", name));
                }

                #endregion

                ((IInitializingObject)target).AfterPropertiesSet();
            }
            if (StringUtils.HasText(definition.InitMethodName))
            {
                #region Instrumentation

                if (log.IsDebugEnabled)
                {
                    log.Debug(
                            string.Format(CultureInfo.InvariantCulture, "Calling custom init method '{0} on object with name '{1}'.",
                                          definition.InitMethodName, name));
                }

                #endregion

                try
                {
                    MethodInfo targetMethod = target.GetType().GetMethod(definition.InitMethodName, MethodResolutionFlags, null, Type.EmptyTypes, null);
                    if (targetMethod == null)
                    {
                        throw new ObjectCreationException(definition.ResourceDescription, name,
                                                          "Could not find the named initialization method '" + definition.InitMethodName + "'.");
                    }
                    targetMethod.Invoke(target, ObjectUtils.EmptyObjects);
                }
                catch (TargetInvocationException ex)
                {
                    throw new ObjectCreationException(definition.ResourceDescription, name,
                                                      "Initialization method '" + definition.InitMethodName + "' threw exception", ex.GetBaseException());
                }
                catch (Exception ex)
                {
                    throw new ObjectCreationException(definition.ResourceDescription, name,
                                                      "Invocation of initialization method '" + definition.InitMethodName + "' failed", ex);
                }
            }
        }

        /// <summary>
        /// Invoke the specified custom destroy method on the given object.
        /// </summary>
        /// <remarks>
        /// <p>
        /// This implementation invokes a no-arg method if found, else checking
        /// for a method with a single boolean argument (passing in "true",
        /// assuming a "force" parameter), else logging an error.
        /// </p>
        /// <p>
        /// Can be overridden in subclasses for custom resolution of destroy
        /// methods with arguments.
        /// </p>
        /// <p>
        /// Custom destroy methods are resolved in a <b>case-insensitive</b> manner.
        /// </p>
        /// </remarks>
        protected virtual void InvokeCustomDestroyMethod(string name, object target, string destroyMethodName)
        {
            bool usingForcingVersion = false;
            MethodInfo targetMethod = target.GetType().GetMethod(destroyMethodName, MethodResolutionFlags, null, Type.EmptyTypes, null);
            if (targetMethod == null)
            {
                // #%&^! try to find the method with a boolean "force" parameter
                targetMethod = target.GetType().GetMethod(destroyMethodName, MethodResolutionFlags, null, new Type[] { typeof(bool) }, null);
                if (targetMethod != null)
                {
                    usingForcingVersion = true;
                }
            }
            if (targetMethod == null)
            {
                #region Instrumentation

                log.Error("Couldn't find a method named '" + destroyMethodName + "' on object with name '" + name + "'");

                #endregion
            }
            else
            {
                object[] args = usingForcingVersion ? new object[] { true } : ObjectUtils.EmptyObjects;
                try
                {
                    targetMethod.Invoke(target, args);
                }
                catch (TargetInvocationException ex)
                {
                    #region Instrumentation

                    log.Error("Couldn't invoke destroy method '" + destroyMethodName + "' of object with name '" + name + "'", ex.GetBaseException());

                    #endregion
                }
                catch (Exception ex)
                {
                    LogExceptionRaisedByCustomDestroyMethodInvocation(destroyMethodName, name, ex);
                }
            }
        }

        private void LogExceptionRaisedByCustomDestroyMethodInvocation(string destroyMethodName, string name, Exception ex)
        {
            log.Error(
                    string.Format(CultureInfo.InvariantCulture, "Couldn't invoke destroy method '{0}' of object with name '{1}'.", destroyMethodName, name),
                    ex);
        }

        /// <summary>
        /// Destroy the target object.
        /// </summary>
        /// <remarks>
        /// <p>
        /// Must destroy objects that depend on the given object before the object itself.
        /// Should not throw any exceptions.
        /// </p>
        /// </remarks>
        /// <param name="name">
        /// The name of the object.
        /// </param>
        /// <param name="target">
        /// The target object instance to destroyed.
        /// </param>
        protected override void DestroyObject(string name, object target)
        {
            #region Instrumentation

            if (log.IsDebugEnabled)
            {
                log.Debug("Destroying dependant objects for object '" + name + "'");
            }

            #endregion

            DestroyDependantObjects(name);
            if (target is IDisposable)
            {
                #region Instrumentation

                if (log.IsDebugEnabled)
                {
                    log.Debug(string.Format(CultureInfo.InvariantCulture, "Calling Dispose () on object with name '{0}'.", name));
                }

                #endregion

                try
                {
                    ((IDisposable)target).Dispose();
                }
                catch (Exception ex)
                {
                    #region Instrumentation

                    log.Error("Destroy() on object with name '" + name + "' threw an exception.", ex);

                    #endregion
                }
            }
            RootObjectDefinition rootDefinition = GetMergedObjectDefinition(name, false);
            if (rootDefinition != null && StringUtils.HasText(rootDefinition.DestroyMethodName))
            {
                #region Instrumentation

                if (log.IsDebugEnabled)
                {
                    log.Debug("Calling custom destroy method '" + rootDefinition.DestroyMethodName + "' on object with name '" + name + "'.");
                }

                #endregion

                InvokeCustomDestroyMethod(name, target, rootDefinition.DestroyMethodName);
            }
        }

        /// <summary>
        /// Destroys all of the objects registered as dependant on the 
        /// object (definition) identified by the supplied <paramref name="name"/>. 
        /// </summary>
        /// <param name="name">
        /// The name of the root object (definition) that is itself being destroyed.
        /// </param>
        private void DestroyDependantObjects(string name)
        {
            string[] dependingObjects = GetDependingObjectNames(name);
            foreach (string doName in dependingObjects)
            {
                DestroySingleton(doName);
            }
        }

        ///// <summary>
        ///// Given a property value, return a value, resolving any references to other
        ///// objects in the factory if necessary.
        ///// </summary>
        ///// <remarks>
        ///// <p>
        ///// The value could be :
        ///// <list type="bullet">
        ///// <item>
        ///// <p>
        ///// An <see cref="Spring.Objects.Factory.Config.IObjectDefinition"/>,
        ///// which leads to the creation of a corresponding new object instance.
        ///// Singleton flags and names of such "inner objects" are always ignored: inner objects
        ///// are anonymous prototypes.
        ///// </p>
        ///// </item>
        ///// <item>
        ///// <p>
        ///// A <see cref="Spring.Objects.Factory.Config.RuntimeObjectReference"/>, which must
        ///// be resolved.
        ///// </p>
        ///// </item>
        ///// <item>
        ///// <p>
        ///// An <see cref="Spring.Objects.Factory.Support.IManagedCollection"/>. This is a
        ///// special placeholder collection that may contain
        ///// <see cref="Spring.Objects.Factory.Config.RuntimeObjectReference"/>s or
        ///// collections that will need to be resolved.
        ///// </p>
        ///// </item>
        ///// <item>
        ///// <p>
        ///// An ordinary object or <see langword="null"/>, in which case it's left alone.
        ///// </p>
        ///// </item>
        ///// </list>
        ///// </p>
        ///// </remarks>
        ///// <param name="name">
        ///// The name of the object that is having the value of one of its properties resolved.
        ///// </param>
        ///// <param name="definition">
        ///// The definition of the named object.
        ///// </param>
        ///// <param name="argumentName">
        ///// The name of the property the value of which is being resolved.
        ///// </param>
        ///// <param name="argumentValue">
        ///// The value of the property that is being resolved.
        ///// </param>
//        protected object ResolveValueIfNecessary(string name, RootObjectDefinition definition, string argumentName, object argumentValue)
//        {
//            object resolvedValue = null;
//
//            AssertUtils.ArgumentNotNull(resolvedValue, "test");
//
//            // we must check the argument value to see whether it requires a runtime
//            // reference to another object to be resolved.
//            // if it does, we'll attempt to instantiate the object and set the reference.
//            if (RemotingServices.IsTransparentProxy(argumentValue))
//            {
//                resolvedValue = argumentValue;
//            }
//            else if (argumentValue is ObjectDefinitionHolder)
//            {
//                // contains an IObjectDefinition with name and aliases...
//                ObjectDefinitionHolder holder = (ObjectDefinitionHolder)argumentValue;
//                resolvedValue = ResolveInnerObjectDefinition(name, holder.ObjectName, argumentName, holder.ObjectDefinition, definition.IsSingleton);
//            }
//            else if (argumentValue is IObjectDefinition)
//            {
//                // resolve plain IObjectDefinition, without contained name: use dummy name... 
//                IObjectDefinition def = (IObjectDefinition)argumentValue;
//                resolvedValue = ResolveInnerObjectDefinition(name, "(inner object)", argumentName, def, definition.IsSingleton);
//
//            }
//            else if (argumentValue is RuntimeObjectReference)
//            {
//                RuntimeObjectReference roref = (RuntimeObjectReference)argumentValue;
//                resolvedValue = ResolveReference(definition, name, argumentName, roref);
//            }
//            else if (argumentValue is ExpressionHolder)
//            {
//                ExpressionHolder expHolder = (ExpressionHolder)argumentValue;
//                object context = null;
//                IDictionary variables = null;
//
//                if (expHolder.Properties != null)
//                {
//                    PropertyValue contextProperty = expHolder.Properties.GetPropertyValue("Context");
//                    context = contextProperty == null
//                                         ? null
//                                         : ResolveValueIfNecessary2(name, definition, "Context",
//                                                                   contextProperty.Value);
//                    PropertyValue variablesProperty = expHolder.Properties.GetPropertyValue("Variables");
//                    object vars = (variablesProperty == null
//                                                   ? null
//                                                   : ResolveValueIfNecessary2(name, definition, "Variables",
//                                                                             variablesProperty.Value));
//                    if (vars is IDictionary)
//                    {
//                        variables = (IDictionary)vars;
//                    }
//                    else
//                    {
//                        if (vars != null) throw new ArgumentException("'Variables' must resolve to an IDictionary");
//                    }
//                }
//
//                if (variables == null) variables = CollectionsUtil.CreateCaseInsensitiveHashtable();
//                // add 'this' objectfactory reference to variables
//                variables.Add(Expression.ReservedVariableNames.CurrentObjectFactory, this);
//
//                resolvedValue = expHolder.Expression.GetValue(context, variables);
//            }
//            else if (argumentValue is IManagedCollection)
//            {
//                resolvedValue =
//                        ((IManagedCollection)argumentValue).Resolve(name, definition, argumentName,
//                                                                     new ManagedCollectionElementResolver(ResolveValueIfNecessary2));
//            }
//            else if (argumentValue is TypedStringValue)
//            {
//                TypedStringValue tsv = (TypedStringValue)argumentValue;
//                try
//                {
//                    Type resolvedTargetType = ResolveTargetType(tsv);
//                    if (resolvedTargetType != null)
//                    {
//                        resolvedValue = TypeConversionUtils.ConvertValueIfNecessary(tsv.TargetType, tsv.Value, null);
//                    }
//                    else
//                    {
//                        resolvedValue = tsv.Value;
//                    }
//                }
//                catch (Exception ex)
//                {
//                    throw new ObjectCreationException(definition.ResourceDescription, name,
//                                                      "Error converted typed String value for " + argumentName, ex);
//                }
//
//            }
//            else
//            {
//                // no need to resolve value...
//                resolvedValue = argumentValue;
//            }
//            return resolvedValue;
//        }

        ///// <summary>
        ///// Resolve the target type of the passed <see cref="TypedStringValue"/>.
        ///// </summary>
        ///// <param name="value">The <see cref="TypedStringValue"/> who's target type is to be resolved</param>
        ///// <returns>The resolved target type, if any. <see lang="null" /> otherwise.</returns>
//        protected virtual Type ResolveTargetType(TypedStringValue value)
//        {
//            if (value.HasTargetType)
//            {
//                return value.TargetType;
//            }
//            else
//            {
//                return null;
//            }
//        }

        ///// <summary>
        ///// Resolves an inner object definition.
        ///// </summary>
        ///// <param name="name">
        ///// The name of the object that surrounds this inner object definition.
        ///// </param>
        ///// <param name="innerObjectName">
        ///// The name of the inner object definition... note: this is a synthetic
        ///// name assigned by the factory (since it makes no sense for inner object
        ///// definitions to have names).
        ///// </param>
        ///// <param name="argumentName">
        ///// The name of the property the value of which is being resolved.
        ///// </param>
        ///// <param name="definition">
        ///// The definition of the inner object that is to be resolved.
        ///// </param>
        ///// <param name="singletonOwner">
        ///// <see langword="true"/> if the owner of the property is a singleton.
        ///// </param>
        ///// <returns>
        ///// The resolved object as defined by the inner object definition.
        ///// </returns>
//        protected object ResolveInnerObjectDefinition(string name, string innerObjectName, string argumentName, IObjectDefinition definition,
//                                                      bool singletonOwner)
//        {
//            RootObjectDefinition mod = GetMergedObjectDefinition(innerObjectName, definition);
//            mod.IsSingleton = singletonOwner;
//            object instance;
//            object result;
//            try
//            {
//                instance = InstantiateObject(innerObjectName, mod, ObjectUtils.EmptyObjects, false, false);
//                result = GetObjectForInstance(innerObjectName, instance);
//            }
//            catch (ObjectsException ex)
//            {
//                throw ObjectCreationException.GetObjectCreationException(ex, name, argumentName, definition.ResourceDescription, innerObjectName);
//            }
//            if (singletonOwner && instance is IDisposable)
//            {
//                // keep a reference to the inner object instance, to be able to destroy
//                // it on factory shutdown...
//                DisposableInnerObjects.Add(instance);
//            }
//            return result;
//        }

        /// <summary>
        /// Resolve a reference to another object in the factory.
        /// </summary>
        /// <param name="name">
        /// The name of the object that is having the value of one of its properties resolved.
        /// </param>
        /// <param name="definition">
        /// The definition of the named object.
        /// </param>
        /// <param name="argumentName">
        /// The name of the property the value of which is being resolved.
        /// </param>
        /// <param name="reference">
        /// The runtime reference containing the value of the property.
        /// </param>
        /// <returns>A reference to another object in the factory.</returns>
        protected object ResolveReference(IConfigurableObjectDefinition definition, string name, string argumentName, RuntimeObjectReference reference)
        {
            #region Instrumentation

            if (log.IsDebugEnabled)
            {
                log.Debug(
                        string.Format(CultureInfo.InvariantCulture, "Resolving reference from property '{0}' in object '{1}' to object '{2}'.",
                                      argumentName, name, reference.ObjectName));
            }

            #endregion

            try
            {
                if (reference.IsToParent)
                {
                    if (null == ParentObjectFactory)
                    {
                        throw new ObjectCreationException(definition.ResourceDescription, name,
                                                          string.Format(
                                                                  "Can't resolve reference to '{0}' in parent factory: " + "no parent factory available.",
                                                                  reference.ObjectName));
                    }
                    return ParentObjectFactory.GetObject(reference.ObjectName);
                }
                return GetObject(reference.ObjectName);
            }
            catch (ObjectsException ex)
            {
                throw ObjectCreationException.GetObjectCreationException(ex, name, argumentName, definition.ResourceDescription, reference.ObjectName);
            }
        }

        /// <summary>
        /// Find object instances that match the required <see cref="System.Type"/>.
        /// </summary>
        /// <remarks>
        /// <p>
        /// Called by autowiring. If a subclass cannot obtain information about object
        /// names by <see cref="System.Type"/>, a corresponding exception should be thrown.
        /// </p>
        /// </remarks>
        /// <param name="requiredType">
        /// The <see cref="System.Type"/> of the objects to look up.
        /// </param>
        /// <returns>
        /// An <see cref="IDictionary"/> of object names and object
        /// instances that match the required <see cref="System.Type"/>, or
        /// <see langword="null"/> if none are found.
        /// </returns>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// In case of errors.
        /// </exception>
        protected abstract IDictionary FindMatchingObjects(Type requiredType);

        /// <summary>
        /// Return the names of the objects that depend on the given object.
        /// Called by DestroyObject, to be able to destroy depending objects first.
        /// </summary>
        /// <param name="name">
        /// The name of the object to find depending objects for.
        /// </param>
        /// <returns>
        /// The array of names of depending objects, or the empty string array if none.
        /// </returns>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// In case of errors.
        /// </exception>
        protected abstract string[] GetDependingObjectNames(string name);

        /// <summary>
        /// Injects dependencies into the supplied <paramref name="target"/> instance
        /// using the named object definition.
        /// </summary>
        /// <param name="target">
        /// The object instance that is to be so configured.
        /// </param>
        /// <param name="name">
        /// The name of the object definition expressing the dependencies that are to
        /// be injected into the supplied <parameref name="target"/> instance.
        /// </param>
        /// <seealso cref="Spring.Objects.Factory.IObjectFactory.ConfigureObject(object, string)"/>
        public override object ConfigureObject(object target, string name)
        {
            RootObjectDefinition definition = GetMergedObjectDefinition(name, true);
            if (definition != null)
            {
                return ConfigureObject(name, definition, new ObjectWrapper(target));
            }

            return target;
        }

        /// <summary>
        /// Injects dependencies into the supplied <paramref name="target"/> instance
        /// using the supplied <paramref name="definition"/>.
        /// </summary>
        /// <param name="target">
        /// The object instance that is to be so configured.
        /// </param>
        /// <param name="name">
        /// The name of the object definition expressing the dependencies that are to
        /// be injected into the supplied <parameref name="target"/> instance.
        /// </param>
        /// <param name="definition">
        /// An object definition that should be used to configure object.
        /// </param>
        /// <seealso cref="Spring.Objects.Factory.IObjectFactory.ConfigureObject(object, string)"/>
        public override object ConfigureObject(object target, string name, IObjectDefinition definition)
        {
            return ConfigureObject(name, new RootObjectDefinition(definition), new ObjectWrapper(target));
        }

        /// <summary>
        /// Configures object instance by injecting dependencies, satisfying Spring lifecycle
        /// interfaces and applying object post-processors.
        /// </summary>
        /// <param name="name">
        /// The name of the object definition expressing the dependencies that are to
        /// be injected into the supplied <parameref name="target"/> instance.
        /// </param>
        /// <param name="definition">
        /// An object definition that should be used to configure object.
        /// </param>
        /// <param name="wrapper">
        /// A wrapped object instance that is to be so configured.
        /// </param>
        /// <seealso cref="Spring.Objects.Factory.IObjectFactory.ConfigureObject(object, string)"/>
        protected virtual object ConfigureObject(string name, RootObjectDefinition definition, IObjectWrapper wrapper)
        {
            object instance = wrapper.WrappedInstance;

            #region Instrumentation

            if (log.IsDebugEnabled)
            {
                log.Debug(string.Format("Configuring object using definition '{1}'", instance, name));
            }

            #endregion

            PopulateObject(name, definition, wrapper);
            WireEvents(name, definition, wrapper);

            if (ObjectUtils.IsAssignableAndNotTransparentProxy(typeof(IObjectNameAware), instance))
            {
                #region Instrumentation

                if (log.IsDebugEnabled)
                {
                    log.Debug(string.Format(CultureInfo.InvariantCulture, "Setting the name property on the IObjectNameAware object '{0}'.", name));
                }

                #endregion

                ((IObjectNameAware)instance).ObjectName = name;
            }

            if (ObjectUtils.IsAssignableAndNotTransparentProxy(typeof(IObjectFactoryAware), instance))
            {
                #region Instrumentation

                if (log.IsDebugEnabled)
                {
                    log.Debug(
                            string.Format(CultureInfo.InvariantCulture, "Setting the ObjectFactory property on the IObjectFactoryAware object '{0}'.",
                                          name));
                }

                #endregion

                ((IObjectFactoryAware)instance).ObjectFactory = this;
            }

            instance = ApplyObjectPostProcessorsBeforeInitialization(instance, name);
            InvokeInitMethods(instance, name, definition);
            instance = ApplyObjectPostProcessorsAfterInitialization(instance, name);

            return instance;
        }


        /// <summary>
        /// Applies the <code>PostProcessAfterInitialization</code> callback of all
        /// registered IObjectPostProcessors, giving them a chance to post-process
        /// the object obtained from IFactoryObjects (for example, to auto-proxy them)
        /// </summary>
        /// <param name="instance">The instance obtained from the IFactoryObject.</param>
        /// <param name="objectName">Name of the object.</param>
        /// <returns>The object instance to expose</returns>
        /// <exception cref="ObjectsException">if any post-processing failed.</exception>
        protected override object PostProcessObjectFromFactoryObject(object instance, string objectName)
        {
            return ApplyObjectPostProcessorsAfterInitialization(instance, objectName);
        }

        #endregion

        #region IAutowireCapableObjectFactory Members

        /// <summary>
        /// Create a new object instance of the given class with the specified
        /// autowire strategy.
        /// </summary>
        /// <param name="type">
        /// The <see cref="System.Type"/> of the object to instantiate.
        /// </param>
        /// <param name="autowireMode">
        /// The desired autowiring mode.
        /// </param>
        /// <param name="dependencyCheck">
        /// Whether to perform a dependency check for objects (not applicable to
        /// autowiring a constructor, thus ignored there).
        /// </param>
        /// <returns>The new object instance.</returns>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// If the wiring fails.
        /// </exception>
        /// <seealso cref="Spring.Objects.Factory.Config.AutoWiringMode"/>
        public virtual object Autowire(Type type, AutoWiringMode autowireMode, bool dependencyCheck)
        {
            RootObjectDefinition rod = new RootObjectDefinition(type, autowireMode, dependencyCheck);
            if (rod.ResolvedAutowireMode == AutoWiringMode.Constructor)
            {
                return AutowireConstructor(type.Name, rod, null, null).WrappedInstance;
            }

            object obj = InstantiationStrategy.Instantiate(rod, string.Empty, this);
            PopulateObject(obj.GetType().Name, rod, new ObjectWrapper(obj));
            return obj;
        }

        /// <summary>
        /// Autowire the object properties of the given object instance by name or
        /// <see cref="System.Type"/>.
        /// </summary>
        /// <param name="instance">
        /// The existing object instance.
        /// </param>
        /// <param name="autowireMode">
        /// The desired autowiring mode.
        /// </param>
        /// <param name="dependencyCheck">
        /// Whether to perform a dependency check for the object.
        /// </param>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// If the wiring fails.
        /// </exception>
        /// <exception cref="System.ArgumentException">
        /// If the supplied <paramref name="autowireMode"/> is not one of the
        /// <seealso cref="Spring.Objects.Factory.Config.AutoWiringMode.ByName"/> or
        /// <seealso cref="Spring.Objects.Factory.Config.AutoWiringMode.ByType"/>
        /// values.
        /// </exception>
        /// <seealso cref="Spring.Objects.Factory.Config.AutoWiringMode"/>
        public virtual void AutowireObjectProperties(object instance, AutoWiringMode autowireMode, bool dependencyCheck)
        {
            if (autowireMode != AutoWiringMode.ByName && autowireMode != AutoWiringMode.ByType)
            {
                throw new ArgumentException("Just AutoWiringMode.ByName and AutoWiringMode.ByType allowed.");
            }

            RootObjectDefinition rod = new RootObjectDefinition(instance.GetType(), autowireMode, dependencyCheck);
            PopulateObject(instance.GetType().Name, rod, new ObjectWrapper(instance));
        }

        /// <summary>
        /// Apply <see cref="Spring.Objects.Factory.Config.IObjectPostProcessor"/>s
        /// to the given existing object instance, invoking their
        /// <see cref="Spring.Objects.Factory.Config.IObjectPostProcessor.PostProcessBeforeInitialization"/>
        /// methods.
        /// </summary>
        /// <param name="instance">
        /// The existing object instance.
        /// </param>
        /// <param name="name">
        /// The name of the object.
        /// </param>
        /// <returns>
        /// The object instance to use, either the original or a wrapped one.
        /// </returns>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// If any post-processing failed.
        /// </exception>
        /// <seealso cref="Spring.Objects.Factory.Config.IObjectPostProcessor.PostProcessBeforeInitialization"/>
        public virtual object ApplyObjectPostProcessorsBeforeInitialization(object instance, string name)
        {
            #region Instrumentation

            if (log.IsDebugEnabled)
            {
                log.Debug("Invoking IObjectPostProcessors before initialization of object '" + name + "'");
            }

            #endregion

            object result = instance;
            foreach (IObjectPostProcessor objectProcessor in ObjectPostProcessors)
            {
                result = objectProcessor.PostProcessBeforeInitialization(result, name);
                if (result == null)
                {
                    throw new ObjectCreationException(name,
                            string.Format(CultureInfo.InvariantCulture,
                                          "PostProcessBeforeInitialization method of IObjectPostProcessor [{0}] "
                                          + " returned null for object [{1}] with name '{2}'.", objectProcessor, instance, name));
                }
            }
            return result;
        }

        /// <summary>
        /// Apply <see cref="Spring.Objects.Factory.Config.IObjectPostProcessor"/>s
        /// to the given existing object instance, invoking their
        /// <see cref="Spring.Objects.Factory.Config.IObjectPostProcessor.PostProcessAfterInitialization"/>
        /// methods.
        /// </summary>
        /// <param name="instance">
        /// The existing object instance.
        /// </param>
        /// <param name="name">
        /// The name of the object.
        /// </param>
        /// <returns>
        /// The object instance to use, either the original or a wrapped one.
        /// </returns>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// If any post-processing failed.
        /// </exception>
        /// <seealso cref="Spring.Objects.Factory.Config.IObjectPostProcessor.PostProcessAfterInitialization"/>
        public virtual object ApplyObjectPostProcessorsAfterInitialization(object instance, string name)
        {
            #region Instrumentation

            if (log.IsDebugEnabled)
            {
                log.Debug("Invoking IObjectPostProcessors after initialization of object '" + name + "'");
            }

            #endregion

            object result = instance;
            foreach (IObjectPostProcessor objectProcessor in ObjectPostProcessors)
            {
                result = objectProcessor.PostProcessAfterInitialization(result, name);
                if (result == null)
                {
                    throw new ObjectCreationException(name,
                            string.Format(CultureInfo.InvariantCulture,
                                          "PostProcessAfterInitialization method of IObjectPostProcessor [{0}] "
                                          + " returned null for object [{1}] with name [{2}].", objectProcessor, instance, name));
                }
            }
            return result;
        }

        /// <summary>
        /// Resolve the specified dependency against the objects defined in this factory.
        /// </summary>
        /// <param name="descriptor">The descriptor for the dependency.</param>
        /// <param name="objectName">Name of the object which declares the present dependency.</param>
        /// <param name="autowiredObjectNames">A list that all names of autowired object (used for
        /// resolving the present dependency) are supposed to be added to.</param>
        /// <returns>
        /// the resolved object, or <code>null</code> if none found
        /// </returns>
        /// <exception cref="ObjectsException">if dependency resolution failed</exception>
        public abstract object ResolveDependency(DependencyDescriptor descriptor, string objectName,
                                                 IList autowiredObjectNames);

        #endregion

        #region Fields

        private IInstantiationStrategy instantiationStrategy = new MethodInjectingInstantiationStrategy();

        /// <summary>
        /// Cache of filtered PropertyInfos: object Type -> PropertyInfo array 
        /// </summary>
        private IDictionary filteredPropertyDescriptorsCache = new Hashtable();

        /// <summary>
        /// Dependency interfaces to ignore on dependency check and autowire, as Set of
        /// Class objects. By default, only the IObjectFactoryAware and IObjectNameAware 
        /// interfaces are ignored.
        /// </summary>
        private ISet ignoredDependencyInterfaces = new HybridSet();

        #endregion
    }

    internal class UnsatisfiedDependencyExceptionData
    {
        private int parameterIndex;
        private Type parameterType;
        private string errorMessage;

        public UnsatisfiedDependencyExceptionData(int parameterIndex, Type parameterType, string errorMessage)
        {
            this.parameterIndex = parameterIndex;
            this.parameterType = parameterType;
            this.errorMessage = errorMessage;
        }

        public int ParameterIndex
        {
            get { return parameterIndex; }
        }

        public Type ParameterType
        {
            get { return parameterType; }
        }

        public string ErrorMessage
        {
            get { return errorMessage; }
        }
    }
}
