#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;

#endregion

namespace Spring.Objects.Factory
{
	/// <summary>
	/// The root interface for accessing a Spring.NET IoC container.
	/// </summary>
	/// <remarks>
	/// <para>
	/// This is the basic client view of a Spring.NET IoC container; further interfaces
	/// such as <see cref="Spring.Objects.Factory.IListableObjectFactory"/> and
	/// <see cref="Spring.Objects.Factory.Config.IConfigurableObjectFactory"/>
	/// are available for specific purposes such as enumeration and configuration.
	/// </para>
	/// <para>
	/// This is the root interface to be implemented by objects that can hold a number
	/// of object definitions, each uniquely identified by a <see cref="System.String"/>
	/// name. An independent instance of any of these objects can be obtained
	/// (the Prototype design pattern), or a single shared instance can be obtained
	/// (a superior alternative to the Singleton design pattern, in which the instance is a
	/// singleton in the scope of the factory). Which type of instance
	/// will be returned depends on the object factory configuration - the API is the same.
	/// The Singleton approach is more useful and hence more common in practice.
	/// </para>
	/// <para>
	/// The point of this approach is that the IObjectFactory is a central registry of
	/// application components, and centralizes the configuring of application components
	/// (no more do individual objects need to read properties files, for example).
	/// See chapters 4 and 11 of "Expert One-on-One J2EE Design and Development" for a
	/// discussion of the benefits of this approach.
	/// </para>
	/// <para>
	/// Normally an IObjectFactory will load object definitions stored in a configuration
	/// source (such as an XML document), and use the <see cref="Spring.Objects"/>
	/// namespace to configure the objects. However, an implementation could simply return
	/// .NET objects it creates as necessary directly in .NET code. There are no
	/// constraints on how the definitions could be stored: LDAP, RDBMS, XML, properties
	/// file etc. Implementations are encouraged to support references amongst objects,
	/// to either Singletons or Prototypes.
	/// </para>
	/// <para>
	/// In contrast to the methods in
	/// <see cref="Spring.Objects.Factory.IListableObjectFactory"/>, all of the methods
	/// in this interface will also check parent factories if this is an
	/// <see cref="Spring.Objects.Factory.IHierarchicalObjectFactory"/>. If an object is
	/// not found in this factory instance, the immediate parent is asked. Objects in
	/// this factory instance are supposed to override objects of the same name in any
	/// parent factory.
	/// </para>
	/// <para>
	/// Object factories are supposed to support the standard object lifecycle interfaces
	/// as far as possible. The maximum set of initialization methods and their standard
	/// order is:
	/// </para>
	/// <para>
	/// <list type="bullet">
	/// <item>
	/// <description>
	/// <see cref="Spring.Objects.Factory.IObjectNameAware"/>'s
	/// <see cref="Spring.Objects.Factory.IObjectNameAware.ObjectName"/> property.
	/// </description>
	/// </item>
	/// <item>
	/// <description>
	/// <see cref="Spring.Objects.Factory.IObjectFactoryAware"/>'s
	/// <see cref="Spring.Objects.Factory.IObjectFactoryAware.ObjectFactory"/> property.
	/// </description>
	/// </item>
	/// <item>
	/// <description>
	/// <see cref="Spring.Context.IApplicationContextAware.ApplicationContext"/>
	/// (only applicable if running within an <see cref="Spring.Context.IApplicationContext"/>).
	/// </description>
	/// </item>
	/// <item>
	/// <description>
	/// The
	/// <see cref="Spring.Objects.Factory.Config.IObjectPostProcessor.PostProcessBeforeInitialization"/>
	/// method of
	/// <see cref="Spring.Objects.Factory.Config.IObjectPostProcessor"/>s.
	/// </description>
	/// </item>
	/// <item>
	/// <description>
	/// <see cref="Spring.Objects.Factory.IInitializingObject"/>'s
	/// <see cref="Spring.Objects.Factory.IInitializingObject.AfterPropertiesSet"/> method.
	/// </description>
	/// </item>
	/// <item>
	/// <description>
	/// A custom init-method definition.
	/// </description>
	/// </item>
	/// <item>
	/// <description>
	/// The
	/// <see cref="Spring.Objects.Factory.Config.IObjectPostProcessor.PostProcessAfterInitialization"/>
	/// method of
	/// <see cref="Spring.Objects.Factory.Config.IObjectPostProcessor"/>s.
	/// </description>
	/// </item>
	/// </list>
	/// </para>
	/// <p/>
	/// <para>
	/// On shutdown of an object factory, the following lifecycle methods apply:
	/// </para>
	/// <para>
	/// <list type="bullet">
	/// <item>
	/// <description>
	/// <see cref="System.IDisposable"/>'s
	/// <see cref="System.IDisposable.Dispose"/> method.
	/// </description>
	/// </item>
	/// <item>
	/// <description>
	/// A custom destroy-method definition.
	/// </description>
	/// </item>
	/// </list>
	/// </para>
	/// </remarks>
	/// <author>Rod Johnson</author>
	/// <author>Juergen Hoeller</author>
	/// <author>Rick Evans (.NET)</author>
	public interface IObjectFactory : IDisposable
	{
        /// <summary>
        /// Determine whether this object factory treats object names case-sensitive or not.
        /// </summary>
        bool IsCaseSensitive { get; }

        /// <summary>
		/// Is this object a singleton?
		/// </summary>
		/// <remarks>
		/// <para>
		/// That is, will <see cref="Spring.Objects.Factory.IObjectFactory.GetObject(string)"/>
		/// always return the same object?
		/// </para>
		/// <para>
		/// Will ask the parent factory if the object cannot be found in this factory
		/// instance.
		/// </para>
		/// </remarks>
		/// <param name="name">The name of the object to query.</param>
		/// <returns>True if the named object is a singleton.</returns>
		/// <exception cref="Spring.Objects.Factory.NoSuchObjectDefinitionException">
		/// If there's no such object definition.
		/// </exception>
		bool IsSingleton(string name);


        /// <summary>
        /// Determines whether the specified object name is prototype.  That is, will GetObject
        /// always return independent instances?
        /// </summary>
        /// <remarks>This method returning false does not clearly indicate a singleton object.
        /// It indicated non-independent instances, which may correspond to a scoped object as 
        /// well.  use the IsSingleton property to explicitly check for a shared 
        /// singleton instance.
        /// <para>Translates aliases back to the corresponding canonical object name.  Will ask the
        /// parent factory if the object can not be found in this factory instance.
        /// </para>
        /// </remarks>
        /// 
        /// <param name="name">The name of the object to query</param>
        /// <returns>
        /// 	<c>true</c> if the specified object name will always deliver independent instances; otherwise, <c>false</c>.
        /// </returns>
        /// <exception cref="NoSuchObjectDefinitionException">if there is no object with the given name.</exception>
	    bool IsPrototype(string name);

		/// <summary>
		/// Does this object factory contain an object with the given name?
		/// </summary>
		/// <remarks>
		/// <para>
		/// The concrete lookup strategy depends on the implementation. E.g. <see cref="IHierarchicalObjectFactory"/>s
		/// will also search their parent factory if a name isn't found .
		/// </para>
		/// </remarks>
		/// <param name="name">The name of the object to query.</param>
		/// <returns>True if an object with the given name is defined.</returns>
		bool ContainsObject(string name);

		/// <summary>
		/// Return the aliases for the given object name, if defined.
		/// </summary>
		/// <remarks>
		/// <para>
		/// Will ask the parent factory if the object cannot be found in this factory
		/// instance.
		/// </para>
		/// </remarks>
		/// <param name="name">The object name to check for aliases.</param>
		/// <returns>The aliases, or an empty array if none.</returns>
		/// <exception cref="Spring.Objects.Factory.NoSuchObjectDefinitionException">
		/// If there's no such object definition.
		/// </exception>
		string[] GetAliases(string name);

#if !MONO
		/// <summary>
		/// Return an instance (possibly shared or independent) of the given object name.
		/// </summary>
		/// <remarks>
		/// <para>
		/// This method allows an object factory to be used as a replacement for the
		/// Singleton or Prototype design pattern.
		/// </para>
		/// <para>
		/// Note that callers should retain references to returned objects. There is no
		/// guarantee that this method will be implemented to be efficient. For example,
		/// it may be synchronized, or may need to run an RDBMS query.
		/// </para>
		/// <para>
		/// Will ask the parent factory if the object cannot be found in this factory
		/// instance.
		/// </para>
		/// <para>
		/// This is the indexer for the <see cref="Spring.Objects.Factory.IObjectFactory"/>
		/// interface.
		/// </para>
		/// </remarks>
		/// <param name="name">The name of the object to return.</param>
		/// <returns>The instance of the object.</returns>
		/// <exception cref="Spring.Objects.Factory.NoSuchObjectDefinitionException">
		/// If there's no such object definition.
		/// </exception>
		/// <exception cref="Spring.Objects.ObjectsException">
		/// If the object could not be created.
		/// </exception>
#endif
        object this[string name] { get; }

		/// <summary>
		/// Return an instance (possibly shared or independent) of the given object name.
		/// </summary>
		/// <remarks>
		/// <para>
		/// This method allows an object factory to be used as a replacement for the
		/// Singleton or Prototype design pattern.
		/// </para>
		/// <para>
		/// Note that callers should retain references to returned objects. There is no
		/// guarantee that this method will be implemented to be efficient. For example,
		/// it may be synchronized, or may need to run an RDBMS query.
		/// </para>
		/// <para>
		/// Will ask the parent factory if the object cannot be found in this factory
		/// instance.
		/// </para>
		/// </remarks>
		/// <param name="name">The name of the object to return.</param>
		/// <returns>The instance of the object.</returns>
		/// <exception cref="Spring.Objects.Factory.NoSuchObjectDefinitionException">
		/// If there's no such object definition.
		/// </exception>
		/// <exception cref="Spring.Objects.ObjectsException">
		/// If the object could not be created.
		/// </exception>
		object GetObject(string name);
	    
        /// <summary>
        /// Return an instance (possibly shared or independent) of the given object name.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This method allows an object factory to be used as a replacement for the
        /// Singleton or Prototype design pattern.
        /// </para>
        /// <para>
        /// Note that callers should retain references to returned objects. There is no
        /// guarantee that this method will be implemented to be efficient. For example,
        /// it may be synchronized, or may need to run an RDBMS query.
        /// </para>
        /// <para>
        /// Will ask the parent factory if the object cannot be found in this factory
        /// instance.
        /// </para>
        /// </remarks>
        /// <param name="name">The name of the object to return.</param>
        /// <param name="arguments">
        /// The arguments to use if creating a prototype using explicit arguments to
        /// a static factory method. If there is no factory method and the
        /// arguments are not null, then match the argument values by type and
        /// call the object's constructor.
        /// </param>
        /// <returns>The instance of the object.</returns>
        /// <exception cref="Spring.Objects.Factory.NoSuchObjectDefinitionException">
        /// If there's no such object definition.
        /// </exception>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// If the object could not be created.
        /// </exception>
        /// <exception cref="System.ArgumentNullException">
        /// If the supplied <paramref name="name"/> is <see langword="null"/>.
        /// </exception>
        object GetObject(string name, object[] arguments);
	    
        /// <summary>
        /// Return an instance (possibly shared or independent) of the given object name.
        /// </summary>
        /// <param name="name">The name of the object to return.</param>
        /// <param name="requiredType">
        /// The <see cref="System.Type"/> the object may match. Can be an interface or
        /// superclass of the actual class. For example, if the value is the
        /// <see cref="System.Object"/> class, this method will succeed whatever the
        /// class of the returned instance.
        /// </param>
        /// <param name="arguments">
        /// The arguments to use if creating a prototype using explicit arguments to
        /// a <see lang="static"/> factory method. If there is no factory method and the
        /// supplied <paramref name="arguments"/> array is not <see lang="null"/>, then
        /// match the argument values by type and call the object's constructor.
        /// </param>
        /// <returns>The instance of the object.</returns>
        /// <exception cref="Spring.Objects.Factory.NoSuchObjectDefinitionException">
        /// If there's no such object definition.
        /// </exception>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// If the object could not be created.
        /// </exception>
        /// <exception cref="Spring.Objects.Factory.ObjectNotOfRequiredTypeException">
        /// If the object is not of the required type.
        /// </exception>
        /// <exception cref="System.ArgumentNullException">
        /// If the supplied <paramref name="name"/> is <see langword="null"/>.
        /// </exception>
        /// <seealso cref="Spring.Objects.Factory.IObjectFactory.GetObject(string, Type)"/>
        object GetObject(string name, Type requiredType, object[] arguments);

		/// <summary>
		/// Return an instance (possibly shared or independent) of the given object name.
		/// </summary>
		/// <remarks>
		/// <para>
		/// Provides a measure of type safety by throwing an exception if the object is
		/// not of the required <see cref="System.Type"/>.
		/// </para>
		/// <para>
		/// This method allows an object factory to be used as a replacement for the
		/// Singleton or Prototype design pattern.
		/// </para>
		/// <para>
		/// Note that callers should retain references to returned objects. There is no
		/// guarantee that this method will be implemented to be efficient. For example,
		/// it may be synchronized, or may need to run an RDBMS query.
		/// </para>
		/// <para>
		/// Will ask the parent factory if the object cannot be found in this factory
		/// instance.
		/// </para>
		/// </remarks>
		/// <param name="name">The name of the object to return.</param>
		/// <param name="requiredType">
		/// <see cref="System.Type"/> the object may match. Can be an interface or
		/// superclass of the actual class. For example, if the value is the
		/// <see cref="System.Object"/> class, this method will succeed whatever the
		/// class of the returned instance.
		/// </param>
		/// <returns>The instance of the object.</returns>
		/// <exception cref="Spring.Objects.Factory.NoSuchObjectDefinitionException">
		/// If there's no such object definition.
		/// </exception>
		/// <exception cref="Spring.Objects.ObjectsException">
		/// If the object could not be created.
		/// </exception>
		/// <exception cref="Spring.Objects.Factory.ObjectNotOfRequiredTypeException">
		/// If the object is not of the required type.
		/// </exception>
		object GetObject(string name, Type requiredType);

		/// <summary>
		/// Determine the type of the object with the given name.
		/// </summary>
		/// <remarks>
		/// <para>
		/// More specifically, checks the type of object that
		/// <see cref="Spring.Objects.Factory.IObjectFactory.GetObject(string)"/> would return.
		/// For an <see cref="Spring.Objects.Factory.IFactoryObject"/>, returns the type
		/// of object that the <see cref="Spring.Objects.Factory.IFactoryObject"/> creates.
		/// </para>
		/// </remarks>
		/// <param name="name">The name of the object to query.</param>
		/// <returns>
        /// The type of the object or <cref lang="null"/> if not determinable.
        /// </returns>
		Type GetType(string name);



        /// <summary>
        /// Determines whether the object with the given name matches the specified type.
        /// </summary>
        /// <remarks>More specifically, check whether a GetObject call for the given name
        /// would return an object that is assignable to the specified target type.
        /// Translates aliases back to the corresponding canonical bean name.
        /// Will ask the parent factory if the bean cannot be found in this factory instance.
        /// </remarks>
        /// <param name="name">The name of the object to query.</param>
        /// <param name="targetType">Type of the target to match against.</param>
        /// <returns>
        /// 	<c>true</c> if the object type matches; otherwise, <c>false</c>
        /// if it doesn't match or cannot be determined yet.
        /// </returns>
        /// <exception cref="NoSuchObjectDefinitionException">Ff there is no object with the given name
        /// </exception>
	    bool IsTypeMatch(string name, Type targetType);

        /// <summary>
        /// Return an unconfigured(!) instance (possibly shared or independent) of the given object name.
        /// </summary>
        /// <param name="name">The name of the object to return.</param>
        /// <param name="requiredType">
        /// The <see cref="System.Type"/> the object may match. Can be an interface or
        /// superclass of the actual class. For example, if the value is the
        /// <see cref="System.Object"/> class, this method will succeed whatever the
        /// class of the returned instance.
        /// </param>
        /// <param name="arguments">
        /// The arguments to use if creating a prototype using explicit arguments to
        /// a <see lang="static"/> factory method. If there is no factory method and the
        /// supplied <paramref name="arguments"/> array is not <see lang="null"/>, then
        /// match the argument values by type and call the object's constructor.
        /// </param>
        /// <returns>The unconfigured(!) instance of the object.</returns>
        /// <exception cref="Spring.Objects.Factory.NoSuchObjectDefinitionException">
        /// If there's no such object definition.
        /// </exception>
        /// <exception cref="Spring.Objects.ObjectsException">
        /// If the object could not be created.
        /// </exception>
        /// <exception cref="Spring.Objects.Factory.ObjectNotOfRequiredTypeException">
        /// If the object is not of the required type.
        /// </exception>
        /// <exception cref="System.ArgumentNullException">
        /// If the supplied <paramref name="name"/> is <see langword="null"/>.
        /// </exception>
        /// <seealso cref="Spring.Objects.Factory.IObjectFactory.GetObject(string, Type, object[])"/>
        /// <remarks>
        ///  This method will only <b>instantiate</b> the requested object. It does <b>NOT</b> inject any dependencies!
        /// </remarks>
        object CreateObject(string name, Type requiredType, object[] arguments);

		/// <summary>
		/// Injects dependencies into the supplied <paramref name="target"/> instance
		/// using the named object definition.
		/// </summary>
		/// <remarks>
		/// <para>
		/// In addition to being generally useful, typically this method is used to provide
		/// dependency injection functionality for objects that are instantiated outwith the
		/// control of a developer. A case in point is the way that the current (1.1)
		/// ASP.NET classes instantiate web controls... the instantiation takes place within
		/// a private method of a compiled page, and thus cannot be hooked into the
		/// typical Spring.NET IOC container lifecycle for dependency injection.
		/// </para>
		/// </remarks>
		/// <example>
		/// The following code snippet assumes that the instantiated factory instance
		/// has been configured with an object definition named
		/// '<i>ExampleNamespace.BusinessObject</i>' that has been configured to set the
		/// <c>Dao</c> property of any <c>ExampleNamespace.BusinessObject</c> instance
		/// to an instance of an appropriate implementation...
		/// <code language="C#">
		/// namespace ExampleNamespace
		/// {
		///     public class BusinessObject
		///     {
		///         private IDao _dao;
		/// 		
		///         public BusinessObject() {}
		/// 
		///         public IDao Dao
		///         {
		///			    get { return _dao;	}
		///             set { _dao = value; }
		///         }
		///     }
		/// }
		/// </code>
		/// with the corresponding driver code looking like so...
		/// <code language="C#">
		/// IObjectFactory factory = GetAnIObjectFactoryImplementation();
		/// BusinessObject instance = new BusinessObject();
		/// factory.ConfigureObject(instance, "object_definition_name");
		/// // at this point the dependencies for the 'instance' object will have been resolved...
		/// </code>
		/// </example>
		/// <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>
		/// <exception cref="Spring.Objects.Factory.NoSuchObjectDefinitionException">
		/// If there is no object definition for the supplied <paramref name="name"/>.
		/// </exception>
		/// <exception cref="Spring.Objects.ObjectsException">
		/// If any of the target object's dependencies could not be created.
		/// </exception>
		object ConfigureObject(object target, string name);
	}
}