// @HEADER
// ***********************************************************************
//
//                    Teuchos: Common Tools Package
//                 Copyright (2004) Sandia Corporation
//
// Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
// license for use of this work by or on behalf of the U.S. Government.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the Corporation nor the names of the
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Questions? Contact Michael A. Heroux (maherou@sandia.gov)
//
// ***********************************************************************
// @HEADER

#ifndef TEUCHOS_ABSTRACT_FACTORY_STD_HPP
#define TEUCHOS_ABSTRACT_FACTORY_STD_HPP

#include "Teuchos_AbstractFactory.hpp"

namespace Teuchos {

/** \brief Default post-modification policy class for
 * <tt>AbstractFactorStd</tt> which does nothing!
 */
template<class T_impl>
class PostModNothing {
public:
	/** \brief . */
	void initialize(T_impl* p) const {} // required!
};

/** \brief Default allocation policy class for
 * <tt>AbstractFactoryStd</tt> which returns <tt>new T_impl()</tt>.
 */
template<class T_impl>
class AllocatorNew {
public:
  /** \brief . */
  typedef Teuchos::RCP<T_impl>  ptr_t;                         // required!
  /** \brief . */
  const ptr_t allocate() const { return Teuchos::rcp(new T_impl()); }  // required!
};

/** \brief Simple, templated concrete subclass of universal "Abstract
 * Factory" interface for the creation of objects.
 *
 * This concrete subclass represents a general
 * <tt>AbstractFactory</tt> subclass that can be modified through
 * policy template parameters.  This class is templated on the
 * interface type <tt>T_itfc</tt> that is exposed by the
 * <tt>AbstractFactory<T_itfc></tt> base interface and by a (concrete)
 * implementation type <tt>T_impl</tt>.  The most typical use of this
 * subclass is to use a concrete, default-constructable subclass for
 * <tt>T_impl</tt> and then to simply instantiate a concrete abstract
 * factory for that class using:

 \verbatim

 Teuchos::AbstractFactoryStd<T_itfc,T_impl>  abstractFactory;
 \endverbatim
 *
 * For this default usage, The only requirements for the derived classes type
 * <tt>T_impl</tt> is that it allow the default constructor
 * <tt>T_impl::T_impl()</tt> (i.e. dont make <tt>T_impl::T_impl()</tt>
 * private) and that it allow <tt>T_impl::new()</tt> and
 * <tt>T_impl::delete</tt> (i.e.  don't make them private functions, see
 * Meyers, More Effective C++, Item 27).

 * However, this subclass is also templated on two other policy types that
 * allow a modification on how objects are created and destroyed.  The first
 * templated policy type, <tt>T_PostMod</tt>, defines how an object is
 * modified after it is initially created.  The second templated policy type,
 * <tt>T_Allocator</tt>, defines exactly how an object is created (and
 * therefore also how it is destroyed).  These type two policy classes are
 * described in more detail below.
 *
 * The type <tt>T_PostMod</tt> is responsible for performing any post
 * modifications on a dynamically allocated object before returning it from
 * <tt>create()</tt>.  The requirements for the type <tt>T_PostMod</tt> are
 * that it has a default constructor, a copy constructor and a method
 * <tt>T_PostMod::initialize(T_itfc2*) const</tt> that will perform any
 * required post modifications (initializations).  The type <tt>T_itfc2</tt>
 * argument for this function must be a base class of <tt>T_impl</tt> of
 * course.  The default type for <tt>T_PostMod</tt> is
 * <tt>PostModNothing</tt><tt><T_impl></tt> which does nothing.
 *
 * The type <tt>T_Allocator</tt> allows for specialized memory allocation and
 * cleanup.  This type must allow the default constructor and copy constructor
 * and have a method <tt>Teuchos::RCP<T_impl> T_Allocator::allocate()
 * const</tt> which creates a smart reference-counted pointer to the allocated
 * object.  Also, in returning a <tt>RCP<></tt> object, the client can
 * set a deallocatioin policy object that can specialize the deallocation of
 * the object (see <tt>RCP</tt>).  In defining a specialized
 * <tt>T_Allocator</tt> class, the client can all initialize the object using
 * more than just the default constructor.  Therefore, if the client provides
 * a specialized <tt>T_Allocator</tt> class, there are no restrictions on the
 * class <tt>T_impl</tt> (i.e. does not have to have a default constructor or
 * allow <tt>new</tt> or <tt>delete</tt>).  The default class for
 * <tt>T_Allocator</tt> is <tt>AllocatorNew</tt><tt><T_impl></tt> who's
 * <tt>allocate()</tt> function just returns <tt>rcp(new T_impl())</tt>.
 *
 * Since the <tt>T_Allocator</tt> class can specialize both the memory
 * management and can initialize the object using more that the default
 * constructor, the class <tt>T_PostMod</tt> may seem unecessary.  However, it
 * is more likely that the client will want to define an initialization for a
 * set of classes through an abstract interface and can not for a particular
 * concrete subclass.  Also the initialization for an object can be orthogonal
 * to how it is created and destroyed, thus the two classes <tt>T_PostMod</tt>
 * and <tt>T_Allocator</tt> are both needed for a more general implementation.
 */
template<class T_itfc, class T_impl
				 ,class T_PostMod = PostModNothing<T_impl>
				 ,class T_Allocator = AllocatorNew<T_impl>
        >
class AbstractFactoryStd : public AbstractFactory<T_itfc> {
public:

	typedef typename Teuchos::AbstractFactory<T_itfc>::obj_ptr_t   obj_ptr_t;  // RAB: 20030916: G++ 3.2 complains without this

  /** \brief . */
  AbstractFactoryStd( const T_PostMod& post_mod = T_PostMod(), const T_Allocator& alloc = T_Allocator() );

  /** @name Overriden from AbstractFactory */
  //@{
  /** \brief . */
  obj_ptr_t create() const;
  //@}

private:
  T_PostMod    post_mod_;
  T_Allocator  alloc_;

};


/** \brief Nonmember constructor for an standar abstract factory object.
 *
 * \relates AbstractFactoryStd
 */
template<class T_itfc, class T_impl>
RCP<const AbstractFactory<T_itfc> >
abstractFactoryStd()
{
	return rcp(
		new AbstractFactoryStd<T_itfc,T_impl,PostModNothing<T_impl>,AllocatorNew<T_impl> >()
		);
}


/** \brief Nonmember constructor for an standar abstract factory object.
 *
 * \relates AbstractFactoryStd
 */
template<class T_itfc, class T_impl, class T_Allocator>
RCP<const AbstractFactory<T_itfc> >
abstractFactoryStd( const T_Allocator& alloc = T_Allocator() )
{
	return rcp(
		new AbstractFactoryStd<T_itfc,T_impl,PostModNothing<T_impl>,T_Allocator>(
      PostModNothing<T_impl>(), alloc
      )
		);
}


// ///////////////////////////////////////////////////////
// Template member definitions

template<class T_itfc, class T_impl, class T_PostMod, class T_Allocator>
inline
AbstractFactoryStd<T_itfc,T_impl,T_PostMod,T_Allocator>::AbstractFactoryStd(
	const T_PostMod& post_mod, const T_Allocator& alloc
	)
	:post_mod_(post_mod)
	,alloc_(alloc)
{}

template<class T_itfc, class T_impl, class T_PostMod, class T_Allocator>
inline
typename AbstractFactoryStd<T_itfc,T_impl,T_PostMod,T_Allocator>::obj_ptr_t
AbstractFactoryStd<T_itfc,T_impl,T_PostMod,T_Allocator>::create() const
{
	typename T_Allocator::ptr_t
		ptr = alloc_.allocate();
	post_mod_.initialize(ptr.get());
	return ptr;
}

} // end Teuchos

#endif // TEUCHOS_ABSTRACT_FACTORY_STD_HPP
