#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 Spring.Util;

#endregion

namespace Spring.Objects.Factory.Config
{
	/// <summary>
	/// Returns a value that is an
	/// <see cref="Spring.Objects.Factory.IGenericObjectFactory"/> that
	/// returns an object from an
	/// <see cref="Spring.Objects.Factory.IObjectFactory"/>.
	/// </summary>
	/// <remarks>
	/// <p>
	/// The primary motivation of this class is to avoid having a client object
	/// directly calling the
	/// <see cref="Spring.Objects.Factory.IObjectFactory.GetObject(string)"/>
	/// method to get a prototype object out of an
	/// <see cref="Spring.Objects.Factory.IObjectFactory"/>, which would be a
	/// violation of the inversion of control principle. With the use of this
	/// class, the client object can be fed an
	/// <see cref="Spring.Objects.Factory.IObjectFactory"/> as a property
	/// that directly returns one target <b>prototype</b> object.
	/// </p>
	/// <p>
	/// The object referred to by the value of the
	/// <see cref="Spring.Objects.Factory.Config.ObjectFactoryCreatingFactoryObject.TargetObjectName"/>
	/// property does not have to be a prototype object, but there is little
	/// to no point in using this class in conjunction with a singleton object.
	/// </p>
	/// </remarks>
	/// <example>
	/// <p>
	/// The following XML configuration snippet illustrates the use of this
	/// class...
	/// </p>
	/// <code escaped="true">
	/// <objects>
	///   <!-- prototype object since we have state -->
	///   <object id="MyService" type="A.B.C.MyService" singleton="false"/>
	///   
	///   <object id="MyServiceFactory"
	///		type="Spring.Objects.Factory.Config.ObjectFactoryCreatingFactoryObject">
	///     <property name="TargetObjectName"><idref local="MyService"/></property>
	///   </object>
	///   
	///   <object id="MyClientObject" type="A.B.C.MyClientObject">
	///     <property name="MyServiceFactory" ref="MyServiceFactory"/>
	///   </object>
	/// </objects>
	/// </code>
	/// </example>
	/// <author>Colin Sampaleanu</author>
	/// <author>Simon White (.NET)</author>
    [Serializable]
    public class ObjectFactoryCreatingFactoryObject
		: AbstractFactoryObject, IObjectFactoryAware
	{
		private string _targetObjectName;
		private IObjectFactory _objectFactory;

		#region Properties

		/// <summary>
		/// Sets the name of the target object.
		/// </summary>
		public string TargetObjectName
		{
			set { _targetObjectName = value; }
		}

		/// <summary>
		/// The target factory that will be used to perform the lookup
		/// of the object referred to by the <see cref="TargetObjectName"/>
		/// property.
		/// </summary>
		/// <value>
		/// The owning <see cref="Spring.Objects.Factory.IObjectFactory"/>
		/// (will never be <see langword="null"/>).
		/// </value>
		/// <exception cref="Spring.Objects.ObjectsException">
		/// In case of initialization errors.
		/// </exception>
		/// <seealso cref="Spring.Objects.Factory.IObjectFactoryAware.ObjectFactory"/>
		public IObjectFactory ObjectFactory
		{
			set { this._objectFactory = value; }
		}

		/// <summary>
		/// The <see cref="System.Type"/> of object created by this factory.
		/// </summary>
		public override Type ObjectType
		{
			get { return typeof (IGenericObjectFactory); }
		}

		#endregion

		/// <summary>
		/// Returns an instance of the object factory.
		/// </summary>
		/// <returns>The object factory.</returns>
		protected override object CreateInstance()
		{
			return new GenericObjectFactory(this);
		}

		/// <summary>
		/// Invoked by an <see cref="Spring.Objects.Factory.IObjectFactory"/>
		/// after it has set all supplied object properties.
		/// </summary>
		/// <exception cref="System.Exception">
		/// In the event of misconfiguration (such as failure to set an essential
		/// property) or if initialization fails.
		/// </exception>
		/// <seealso cref="Spring.Objects.Factory.Config.AbstractFactoryObject.AfterPropertiesSet"/>
		public override void AfterPropertiesSet()
		{
			if(StringUtils.IsNullOrEmpty(_targetObjectName)) 
			{
				throw new ArgumentException("The 'TargetObjectName' property must have a value.");
			}
			base.AfterPropertiesSet ();
		}

		private sealed class GenericObjectFactory : IGenericObjectFactory
		{
			private ObjectFactoryCreatingFactoryObject _enclosing;

			/// <summary>
			/// Creates a new instance of the GenericObjectFactory class.
			/// </summary>
			/// <param name="enclosing">
			/// The enclosing
			/// <see cref="Spring.Objects.Factory.Config.ObjectFactoryCreatingFactoryObject"/>.
			/// </param>
			public GenericObjectFactory(
				ObjectFactoryCreatingFactoryObject enclosing)
			{
				_enclosing = enclosing;
			}

			/// <summary>
			/// Returns the object created by the enclosed object factory.
			/// </summary>
			/// <returns>The created object.</returns>
			public object GetObject()
			{
				return _enclosing._objectFactory.GetObject(_enclosing._targetObjectName);
			}
		}
	}
}