/*

Copyright (c) 2002-2005, Yauheni Akhotnikau
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

- 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.

- The name of the author may not be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.

*/

#include <sstream>
#include <algorithm>
#include <functional>

#include <cpp_util_2/h/lexcast.hpp>

#include <oess_1/defs/h/ex.hpp>

#include <oess_1/scheme/h/errno.hpp>

#include <oess_1/scheme/impl/h/serializer_gen.hpp>
#include <oess_1/scheme/impl/h/text_scheme_repres.hpp>

namespace oess_1 {

namespace scheme {

namespace impl {

//
// is_optional_attr
//

/*!
	\since v.1.2.0
	\brief Является ли атрибут опциональным.
*/
inline bool
is_optional_attr( const attr_t & a )
{
	return( a.query_attr_default() &&
		a.query_attr_default()->is_optional() );
}

//
// is_default_attr
//

/*!
	\since v1.2.0
	\brief Есть ли у атрибута значение по-умолчанию.
*/
inline bool
is_default_attr( const attr_t & a )
{
	return ( 0 != a.query_attr_default() );
}

//
// missing_attr_flag_name
//

/*!
	\since v.1.2.0
	\brief Генерация имени флага отсутствия атрибута со
		значением по-умолчанию.
*/
inline std::string
missing_attr_flag_name( const std::string & attr )
{
	return std::string( "is_missing__" ) + attr;
}

//
// make_attr_missing_flags_t
//

/*!
	\since v.1.2.0
	\brief Предикат для std::for_each.

	Если атрибут имеет значение по-умолчанию, то генерирует
	объявление флага отсутствия значения атрибута вида:
\code
bool is_missing__<a.query_name()> = true;
\endcode
*/
class	make_attr_missing_flags_t
:	public std::unary_function< const attr_t &, void >
{
	//! Приемник описания.
	std::ostream & m_out;
	public :
		make_attr_missing_flags_t( std::ostream & out )
		:	m_out( out )
		{}

		result_type
		operator()( argument_type a )
		{
			if( is_default_attr( a ) )
				m_out << "\t" "bool "
					<< missing_attr_flag_name( a.query_name() )
					<< " = true;\n";
		}
};

//
// make_set_default_for_missing_attr_t
//

/*!
	\since v.1.2.0
	\brief Предикат для std::for_each.

	Если атрибут имеет значение по-умолчанию, то генерируется
	код по присвоению атрибуту этого значения.
*/
class	make_set_default_for_missing_attr_t
:	public std::unary_function< const attr_t &, void >
{
	//! Приемник описания.
	std::ostream & m_out;
	public :
		make_set_default_for_missing_attr_t( std::ostream & out )
		:	m_out( out )
		{}

		result_type
		operator()( argument_type a )
		{
			if( is_default_attr( a ) )
				m_out << "\t" "if( "
					<< missing_attr_flag_name( a.query_name() )
					<< " )\n" "\t\t" "o.oess_set_default( \""
					<< a.query_name() << "\" );\n";
		}
};

//
// make_present_if_method_fragment_t
//
/*!
	\since v.1.2.0
	\brief Предикат для std::for_each.

	Если атрибут является опциональным, то генерирует
	фрагмент метода oess_present_if следующего вида:
\code
if( <attr> == a )
	return ( <present_if_pred> );
\endcode

	\attention Может быть порождено исключение, если
		не задан код предиката для C++.
*/
class	make_present_if_method_fragment_t
:	public std::unary_function< const attr_t &, void >
{
	//! Приемник описания.
	std::ostream & m_out;
	public :
		make_present_if_method_fragment_t( std::ostream & out )
		:	m_out( out )
		{}

		result_type
		operator()( argument_type a )
		{
			if( is_optional_attr( a ) )
			{
				try
				{
					m_out << "\t" "if( a == \""
						<< a.query_name() << "\" )\n" "\t\t" "return ( "
						<< a.query_attr_default()->present_if().cpp()
						<< " );\n";
				}
				catch( const std::exception & x )
				{
					OESS_THROW_PHYSIC(
						oess_1::scheme::err::
							c_serialization_code_generation_fail,
						"attribute: " << a.query_name()
							<< ", caused by: "
							<< x.what() )
				}
			}
		}
};

//
// make_set_default_method_fragment_t
//
/*!
	\since v.1.2.0
	\brief Предикат для std::for_each.

	Если атрибут имеет значение по-умолчанию, то генерирует
	фрагмент метода oess_set_default следующего вида:
\code
if( a == <attr> )
	<attr> = <default_value>;
\endcode

	\attention Может быть порождено исключение, если
		не задан код значения по-умолчанию для C++.
*/
class	make_set_default_method_fragment_t
:	public std::unary_function< const attr_t &, void >
{
	//! Приемник описания.
	std::ostream & m_out;
	public :
		make_set_default_method_fragment_t( std::ostream & out )
		:	m_out( out )
		{}

		result_type
		operator()( argument_type a )
		{
			if( is_default_attr( a ) )
			{
				try
				{
					m_out << "\t" "if( a == \""
						<< a.query_name() << "\" )\n" "\t\t"
						<< a.query_name() << " = "
						<< a.query_attr_default()->cpp()
						<< " ;\n";
				}
				catch( const std::exception & x )
				{
					OESS_THROW_PHYSIC(
						oess_1::scheme::err::
							c_serialization_code_generation_fail,
						"attribute: " << a.query_name()
							<< ", caused by: "
							<< x.what() )
				}
			}
		}
};

//
// make_unpack_for_base_t
//
class	make_unpack_for_base_t :
	public std::unary_function< const base_t &, void >
{
	private :
		std::ostream &	m_out;
	public :
		make_unpack_for_base_t( std::ostream & out ) :
			m_out( out ) {
		}

		result_type
		operator()( argument_type a ) {
			const std::string & cpp_type_name =
				a.query_type().query_cpp_mapping().query_name();
			m_out << "\t\t" << cpp_type_name
				<< "::oess_serializer_t::unpack( s, *(("
				<< cpp_type_name << "*) &o), our_subclass_ptr );\n";
		}
};

//
// make_unpack_complete_for_base_t
//
class	make_unpack_complete_for_base_t :
	public std::unary_function< const base_t &, void >
{
	private :
		std::ostream &	m_out;
	public :
		make_unpack_complete_for_base_t( std::ostream & out ) :
			m_out( out ) {
		}

		result_type
		operator()( argument_type a ) {
			const std::string & cpp_type_name =
				a.query_type().query_cpp_mapping().query_name();
			m_out << "\t" << cpp_type_name
				<< "::oess_serializer_t::unpack_complete( s, *(("
				<< cpp_type_name << "*) &o) );\n";
		}
};

//
// attr_type_kind_processor_t
//

/*
	Класс, который определяет параметры для генерации
	сериализации отдельного атрибута по типу атрибута.
*/
class	attr_type_kind_processor_t :
	public type_kind_processor_t
{
	public :
		attr_type_kind_processor_t(
			std::ostream & out,
			// Имя атрибута.
			const std::string & attr_name,
			// Имя типа атрибута.
			const std::string & cpp_type_name,
			bool is_pack_method,
			// Имя типа для шаблонов iobj, iptr и т.д.
			// Формируется в процессе обработки.
			std::string & templ_type_param,
			// Имя объекта для экземпляра шаблона iobj, iptr и т.д.
			// Формируется в процессе обработки.
			std::string & obj_param,
			// Имя типа для параметров Reader, Writter
			// таких шаблонов, как ilist, iset, imap и т.д.
			// Формируется в процессе обработки.
			std::string & templ_reader_param );
		virtual ~attr_type_kind_processor_t();

	protected :
		virtual void
		on_value_kind(
			const attr_type_t & k );

		virtual void
		on_ptr_kind(
			const attr_type_t & k );

		virtual void
		on_extension_kind(
			const attr_type_t & k );

	private :
		std::ostream &	m_out;

		// Имя атрибута.
		const std::string &	m_attr_name;
		// Имя типа атрибута.
		const std::string &	m_cpp_type_name;

		bool	m_is_pack_method;

		// Имя типа для шаблонов iobj, iptr и т.д.
		// Формируется в процессе обработки.
		std::string & m_templ_type_param;
		// Имя объекта для экземпляра шаблона iobj, iptr и т.д.
		// Формируется в процессе обработки.
		std::string & m_obj_param;
		// Имя типа для параметров Reader, Writter
		// таких шаблонов, как ilist, iset, imap и т.д.
		// Формируется в процессе обработки.
		std::string & m_templ_reader_param;

		// Префикс шаблонного типа для ввода/вывода.
		const std::string &
		template_prefix() const;
};

attr_type_kind_processor_t::attr_type_kind_processor_t(
	std::ostream & out,
	const std::string & attr_name,
	const std::string & cpp_type_name,
	bool is_pack_method,
	std::string & templ_type_param,
	std::string & obj_param,
	std::string & templ_reader_param ) :
	m_out( out ),
	m_attr_name( attr_name ),
	m_cpp_type_name( cpp_type_name ),
	m_is_pack_method( is_pack_method ),
	m_templ_type_param( templ_type_param ),
	m_obj_param( obj_param ),
	m_templ_reader_param( templ_reader_param )
{
}

attr_type_kind_processor_t::~attr_type_kind_processor_t()
{
}

void
attr_type_kind_processor_t::on_value_kind(
	const attr_type_t & k )
{
	m_templ_type_param = m_cpp_type_name;
	m_obj_param = std::string( "o." ) + m_attr_name;
	m_templ_reader_param = std::string( "oess_1::stdsn::" ) +
		template_prefix() + "obj_t< " + m_cpp_type_name + " >";
}

void
attr_type_kind_processor_t::on_ptr_kind(
	const attr_type_t & k )
{
	if( single_obj_kind_t::e_id ==
		k.query_cont_kind().query_kind_id() ) {
		m_templ_type_param = std::string( "oess_1::stdsn::" ) +
			template_prefix() + "ptr_t< " + m_cpp_type_name + " >";

		m_out << "\t\t" << m_templ_type_param
			<< " io_ptr( o." << m_attr_name << " );\n";
		m_obj_param = "io_ptr";
	}
	else {
		m_templ_type_param = m_cpp_type_name + " *";
		m_obj_param = std::string( "o." ) + m_attr_name;
		m_templ_reader_param = std::string( "oess_1::stdsn::" ) +
			template_prefix() + "ptr_t< " + m_cpp_type_name + " >";
	}
}

void
attr_type_kind_processor_t::on_extension_kind(
	const attr_type_t & k )
{
	if( single_obj_kind_t::e_id == k.query_cont_kind().query_kind_id() )
	{
		m_templ_type_param = std::string( "oess_1::stdsn::" ) +
			template_prefix() + "extension_of_t< " + m_cpp_type_name + " >";

		m_out << "\t\t" << m_templ_type_param
			<< " io_extension_of( o." << m_attr_name << " );\n";
		m_obj_param = "io_extension_of";
	}
	else {
		m_templ_type_param = m_cpp_type_name + " *";
		m_obj_param = std::string( "o." ) + m_attr_name;
		m_templ_reader_param = std::string( "oess_1::stdsn::" ) +
			template_prefix() + "extension_of_t< " + m_cpp_type_name + " >";
	}
}

const std::string &
attr_type_kind_processor_t::template_prefix() const
{
	static std::string o( "o" );
	static std::string i( "i" );

	return ( m_is_pack_method ? o : i );
}

//
// attr_serialize_processor_t
//

/*
	Класс, который занимается генерацией кода
	сериализации отдельного атрибута.
*/
class	attr_serialize_processor_t :
	public cont_kind_processor_t {
	public :
		attr_serialize_processor_t(
			std::ostream & out,
			// Объекта со значением атрибута.
			const std::string & attr_name,
			// Имя типа объекта со значением атрибута.
			const std::string & cpp_type_name,
			// Имя типа, которое должно указываться
			// третьим параметром шаблонов list, set, map.
			const std::string & templ_reader_param,
			// Имеет значение true, если генерируется метод pack.
			bool is_pack_method );
		virtual ~attr_serialize_processor_t();

	protected :
		virtual void
		on_single_obj_kind(
			const single_obj_kind_t & k );

		virtual void
		on_stl_vector_kind(
			const stl_vector_kind_t & k );

		virtual void
		on_stl_list_kind(
			const stl_list_kind_t & k );

		virtual void
		on_stl_deque_kind(
			const stl_deque_kind_t & k );

		virtual void
		on_stl_set_kind(
			const stl_set_kind_t & k );

		virtual void
		on_stl_multiset_kind(
			const stl_multiset_kind_t & k );

		virtual void
		on_stl_map_kind(
			const stl_map_kind_t & k );

		virtual void
		on_stl_multimap_kind(
			const stl_multimap_kind_t & k );

		virtual void
		on_fixed_vector_kind(
			const fixed_vector_kind_t & k );

	private :
		std::ostream &	m_out;

		const std::string &	m_attr_name;
		const std::string &	m_cpp_type_name;

		// Имя типа, которое должно указываться
		// третьим параметром шаблонов list, set, map.
		const std::string & m_templ_reader_param;

		// Имеет значение true, если генерируется метод pack.
		bool m_is_pack_method;

		// Стандартный размер отступа.
		enum {
			e_std_margin_size = 2
		};

		// Префикс шаблонного типа для ввода/вывода.
		const std::string &
		template_prefix() const;

		// Направление для оператора сдвига (<<,>>).
		const std::string &
		shift_dir() const;

		// Вывод отступа с указанным количеством
		// табуляций.
		void
		print_margin(
			// Величина отступа (количество табуляций).
			size_t margin_size );

		// Генерация кода для сериализации объекта
		// посредством шаблонов iobj_t и oobj_t
		void
		gen_obj_serialization(
			// Величина отступа (количество табуляций).
			size_t margin_size,
			// Имя параметра шаблона.
			const std::string & type_for_templ,
			// Имя параметра объекта.
			const std::string & obj_for_templ );

		// Обработка атрибута, являющегося stl-вектором,
		// stl-списком или stl-deque.
		void
		on_list_templ(
			// Тип STL-контейнера.
			const std::string & stl_cont_name );

		// Обработка атрибута, являющегося stl-множеством
		// или stl-мультимножеством.
		void
		on_set_templ(
			// Тип STL-контейнера.
			const std::string & stl_cont_name );

		// Обработка атрибута, являющегося stl-словарем
		// или stl-мультисловарем.
		void
		on_map_templ(
			// Тип STL-контейнера.
			const std::string & stl_cont_name,
			// Тип ключа.
			const std::string & key_type_name );

		// Обработка атрибута, являющегося вектором
		// фиксированного размера.
		void
		on_fixed_vector(
			// Размерность вектора.
			const std::string & capacity );
};

attr_serialize_processor_t::attr_serialize_processor_t(
	std::ostream & out,
	const std::string & attr_name,
	const std::string & cpp_type_name,
	const std::string & templ_reader_param,
	bool is_pack_method ) :
	m_out( out ),
	m_attr_name( attr_name ),
	m_cpp_type_name( cpp_type_name ),
	m_templ_reader_param( templ_reader_param ),
	m_is_pack_method( is_pack_method )
{
}

attr_serialize_processor_t::~attr_serialize_processor_t()
{
}

void
attr_serialize_processor_t::on_single_obj_kind(
	const single_obj_kind_t & k )
{
	gen_obj_serialization( e_std_margin_size,
		m_cpp_type_name,
		m_attr_name );
}

void
attr_serialize_processor_t::on_stl_vector_kind(
	const stl_vector_kind_t & k )
{
	on_list_templ( "std::vector" );
}

void
attr_serialize_processor_t::on_stl_list_kind(
	const stl_list_kind_t & k )
{
	on_list_templ( "std::list" );
}

void
attr_serialize_processor_t::on_stl_deque_kind(
	const stl_deque_kind_t & k )
{
	on_list_templ( "std::deque" );
}

void
attr_serialize_processor_t::on_stl_set_kind(
	const stl_set_kind_t & k )
{
	on_set_templ( "std::set" );
}

void
attr_serialize_processor_t::on_stl_multiset_kind(
	const stl_multiset_kind_t & k )
{
	on_set_templ( "std::multiset" );
}

void
attr_serialize_processor_t::on_stl_map_kind(
	const stl_map_kind_t & k )
{
	on_map_templ( "std::map", k.query_key_type().query_name() );
}

void
attr_serialize_processor_t::on_stl_multimap_kind(
	const stl_multimap_kind_t & k )
{
	on_map_templ( "std::multimap", k.query_key_type().query_name() );
}

void
attr_serialize_processor_t::on_fixed_vector_kind(
	const fixed_vector_kind_t & k )
{
	std::ostringstream capacity;
	capacity << k.query_capacity();

	on_fixed_vector( capacity.str() );
}

const std::string &
attr_serialize_processor_t::template_prefix() const
{
	static std::string o( "o" );
	static std::string i( "i" );

	return ( m_is_pack_method ? o : i );
}

const std::string &
attr_serialize_processor_t::shift_dir() const
{
	static std::string o( "<<" );
	static std::string i( ">>" );

	return ( m_is_pack_method ? o : i );
}

void
attr_serialize_processor_t::print_margin(
	size_t margin_size )
{
	std::string margin( margin_size, '\t' );
	m_out << margin;
}

void
attr_serialize_processor_t::gen_obj_serialization(
	size_t margin_size,
	const std::string & type_for_templ,
	const std::string & obj_for_templ )
{
	std::string margin( margin_size, '\t' );

	print_margin( margin_size );
	m_out << "oess_1::stdsn::" << template_prefix() << "obj_t< "
		<< type_for_templ << " > io_obj( "
		<< obj_for_templ << " );\n";
	print_margin( margin_size );
	m_out << "s " << shift_dir() << " io_obj;\n";
}

void
attr_serialize_processor_t::on_list_templ(
	const std::string & stl_cont_name )
{
	std::string type_for_templ(
		std::string( "oess_1::stdsn::" ) +
		template_prefix() + "list_t< " +
		stl_cont_name + "< " +
		m_cpp_type_name + " >, " +
		m_cpp_type_name + ", " +
		m_templ_reader_param + " >" );
	std::string obj_for_templ( "io_cont" );

	print_margin( e_std_margin_size );
	m_out << type_for_templ
		<< " " << obj_for_templ
		<< "( " << m_attr_name << " );\n";
	gen_obj_serialization( e_std_margin_size,
		type_for_templ, obj_for_templ );
}

void
attr_serialize_processor_t::on_set_templ(
	const std::string & stl_cont_name )
{
	std::string type_for_templ(
		std::string( "oess_1::stdsn::" ) +
		template_prefix() + "set_t< " +
		stl_cont_name + "< " +
		m_cpp_type_name + " >, " +
		m_cpp_type_name + ", " +
		m_templ_reader_param + " >" );
	std::string obj_for_templ( "io_cont" );

	print_margin( e_std_margin_size );
	m_out << type_for_templ
		<< " " << obj_for_templ
		<< "( " << m_attr_name << " );\n";
	gen_obj_serialization( e_std_margin_size,
		type_for_templ, obj_for_templ );
}

void
attr_serialize_processor_t::on_map_templ(
	const std::string & stl_cont_name,
	const std::string & key_type_name )
{
	std::string type_for_templ(
		std::string( "oess_1::stdsn::" ) +
		template_prefix() + "map_t< " +
		stl_cont_name + "< " +
		key_type_name + ", " +
		m_cpp_type_name + " >, " +
		m_cpp_type_name + ", " +
		m_templ_reader_param + " >" );
	std::string obj_for_templ( "io_cont" );

	print_margin( e_std_margin_size );
	m_out << type_for_templ
		<< " " << obj_for_templ
		<< "( " << m_attr_name << " );\n";
	gen_obj_serialization( e_std_margin_size,
		type_for_templ, obj_for_templ );
}

void
attr_serialize_processor_t::on_fixed_vector(
	const std::string & capacity )
{
	std::string type_for_templ(
		std::string( "oess_1::stdsn::" ) +
		template_prefix() + "fixed_vector_t< " +
		m_cpp_type_name + ", " +
		capacity + ", " +
		m_templ_reader_param + " >" );
	std::string obj_for_templ( "io_cont" );

	print_margin( e_std_margin_size );
	m_out << type_for_templ
		<< " " << obj_for_templ
		<< "( " << m_attr_name << " );\n";
	gen_obj_serialization( e_std_margin_size,
		type_for_templ, obj_for_templ );
}

//
// make_serialize_for_attr_t
//

/*!
	\brief Предикат для std::for_each.

	Генерирует код сериализации/десериализации атрибута.
*/
class	make_serialize_for_attr_t :
	public std::unary_function< const attr_t &, void >
{
	private :
		//! Приемник сгенерированого кода.
		std::ostream &	m_out;

		//! Имеет значение true, если генерируется метод pack.
		bool m_is_pack_method;

		//! Сгенерировать код начала сериализации
		//! опционального атрибута.
		/*!
			\since v.1.2.0
			\note На момент вызова не известно, является ли атрибут
				опциональным.
		*/
		void
		optional_attr_part_1( argument_type a )
		{
			if( is_optional_attr( a ) )
				if( m_is_pack_method )
					m_out << "\t\t"
							"if( s.set_optional_attr_existence( \""
						<< a.query_name() << "\", o.oess_present_if( \""
						<< a.query_name() << "\" ) ) )\n" "\t\t\t" "{\n";
				else
					m_out << "\t\t"
							"if( s.check_optional_attr_existence( \""
						<< a.query_name() << "\" ) )\n" "\t\t\t" "{\n";
		}

		//! Сгенерировать код завершения сериализации
		//! опционального атрибута.
		/*!
			\since v.1.2.0
			\note На момент вызова не известно, является ли атрибут
				опциональным.
		*/
		void
		optional_attr_part_2( argument_type a )
		{
			if( is_optional_attr( a ) )
				m_out << "\t\t" "}\n";
		}

		//! Сгенерировать изменение флага отсутствия
		//! атрибута по-умолчанию.
		/*!
			\since v.1.2.0
			\note На момент вызова не известно, есть ли у атрибута
				значение по-умолчанию.
		*/
		void
		change_missing_attr_flag(
			argument_type a )
		{
			if( !m_is_pack_method && is_default_attr( a ) )
				m_out << "\t\t"
					<< missing_attr_flag_name( a.query_name() )
					<< " = false;\n";
		}

	public :
		make_serialize_for_attr_t(
			std::ostream & out,
			// Имеет значение true, если генерируется метод pack.
			bool is_pack_method ) :
			m_out( out ),
			m_is_pack_method( is_pack_method )
		{
		}

		result_type
		operator()( argument_type a )
		{
			const attr_type_t & attr_type = a.query_attr_type();
			const std::string & cpp_type_name =
				attr_type.query_type().
					query_cpp_mapping().query_name();

			m_out << "\t{\n";

			optional_attr_part_1( a );

			std::string	templ_type_param;
			std::string obj_param;
			std::string cont_templ_reader_param;
			attr_type_kind_processor_t type_kind_processor(
				m_out, a.query_name(), cpp_type_name,
				m_is_pack_method,
				templ_type_param, obj_param,
				cont_templ_reader_param );
			type_kind_processor.process_type_kind( attr_type );

			attr_serialize_processor_t processor(
				m_out, obj_param, templ_type_param,
				cont_templ_reader_param,
				m_is_pack_method );
			processor.process_cont_kind(
				attr_type.query_cont_kind() );

			change_missing_attr_flag( a );

			optional_attr_part_2( a );

			m_out << "\t}\n";
		}
};

//
// make_pack_for_base_t
//

class	make_pack_for_base_t :
	public std::unary_function< const base_t &, void >
{
	private :
		std::ostream &	m_out;
	public :
		make_pack_for_base_t( std::ostream & out ) :
			m_out( out ) {
		}

		result_type
		operator()( argument_type a ) {
			const std::string & cpp_type_name =
				a.query_type().query_cpp_mapping().query_name();
			m_out << "\t\t" << cpp_type_name
				<< "::oess_serializer_t::pack( s, *((const "
				<< cpp_type_name << "*) &o), our_subclass_ptr );\n";
		}
};

//
// make_pack_complete_for_base_t
//

class	make_pack_complete_for_base_t :
	public std::unary_function< const base_t &, void >
{
	private :
		std::ostream &	m_out;
	public :
		make_pack_complete_for_base_t( std::ostream & out ) :
			m_out( out ) {
		}

		result_type
		operator()( argument_type a ) {
			const std::string & cpp_type_name =
				a.query_type().query_cpp_mapping().query_name();
			m_out << "\t" << cpp_type_name
				<< "::oess_serializer_t::pack_complete( s, *((const "
				<< cpp_type_name << "*) &o) );\n";
		}
};

//
// make_cast_for_base_t
//

class	make_cast_for_base_t :
	public std::unary_function< const base_t &, void >
{
	private :
		std::ostream &	m_out;
	public :
		make_cast_for_base_t( std::ostream & out ) :
			m_out( out ) {
		}

		result_type
		operator()( argument_type a ) {
			const std::string & cpp_type_name =
				a.query_type().query_cpp_mapping().query_name();
			m_out << "\tif( 0 != ( p = " << cpp_type_name
				<< "::oess_serializer_t::cast( cast_to, (("
				<< cpp_type_name << "*) o) ) ) )\n"
				"\t\t""return p;\n";
		}
};

//
// generate_first_part
//
static void
generate_first_part(
	const std::string & type_name,
	std::ostream & out )
	{}

//
// generate_attr_unpack
//

/*!
	\since v.1.2.0

	Генерирует код по десериализации атрибутов с учетом расширений.
*/
static void
generate_attr_unpack(
	const type_t & type,
	std::ostream & out )
{
	// Десериализация атрибутов, которые не входят в расширение.
	pseudo_extension_attr_list_t type_attrs( type, 0 );
	std::for_each( type_attrs.begin(), type_attrs.end(),
		make_serialize_for_attr_t( out, false ) );

	unsigned int ext_level;

	// Обрабатываем расширения.
	for( ext_level = 1;
		ext_level <= type.extension_deep();
		++ext_level )
	{
		std::string ext_variable_name( std::string( "e" ) +
			cpp_util_2::slexcast( ext_level ) );

		out << "\t" "{\n"
			"\tstd::auto_ptr< oess_1::stdsn::iextension_t > "
			<< ext_variable_name << "( s.extension_start() );\n"
			"\t" "if( " << ext_variable_name << "->is_present() )\n"
			"\t{\n" "\t\t" "oess_1::stdsn::ient_t & s = *"
			<< ext_variable_name << ";\n";

		pseudo_extension_attr_list_t ext_attrs( type, ext_level );
		std::for_each( ext_attrs.begin(), ext_attrs.end(),
			make_serialize_for_attr_t( out, false ) );
	}

	// Генерация заглушки для будущих расширений.
	if( type.is_extensible() )
	{
		out << "\t" "{\n"
			"\tstd::auto_ptr< oess_1::stdsn::iextension_t > "
			" future_ext( s.extension_start() );\n"
			"\t" "future_ext->unpack_opaque( "
				"o.oess_writeable_unknown_extension() );\n"
			"\t" "s.extension_finish( *future_ext );\n"
			"\t" "}\n";
	}

	for( ext_level = 1;
		ext_level <= type.extension_deep();
		++ext_level )
	{
		std::string ext_variable_name( std::string( "e" ) +
			cpp_util_2::slexcast( ext_level ) );

		out << "\t} /* if( " << ext_variable_name
			<< "->is_present() ) */\n"
			"\ts.extension_finish( *" << ext_variable_name << " );\n"
			"\t} /* " << ext_variable_name << " */\n";
	}
}

//
// generate_attr_pack
//

/*!
	\since v.1.2.0

	Генерирует код по сериализации атрибутов с учетом расширений.
*/
static void
generate_attr_pack(
	const type_t & type,
	std::ostream & out )
{
	// Сериализация атрибутов, которые не входят в расширение.
	pseudo_extension_attr_list_t type_attrs( type, 0 );
	std::for_each( type_attrs.begin(), type_attrs.end(),
		make_serialize_for_attr_t( out, true ) );

	unsigned int ext_level;

	// Обрабатываем расширения.
	for( ext_level = 1;
		ext_level <= type.extension_deep();
		++ext_level )
	{
		std::string ext_variable_name( std::string( "e" ) +
			cpp_util_2::slexcast( ext_level ) );

		out << "\t" "{\n"
			"\tstd::auto_ptr< oess_1::stdsn::oextension_t > "
			<< ext_variable_name << "( s.extension_start() );\n"
			"\t" "{\n"
			"\t" "oess_1::stdsn::oent_t & s = *"
			<< ext_variable_name << ";\n";

		pseudo_extension_attr_list_t ext_attrs( type, ext_level );
		std::for_each( ext_attrs.begin(), ext_attrs.end(),
			make_serialize_for_attr_t( out, true ) );
	}

	// Генерация заглушки для будущих расширений.
	if( type.is_extensible() )
	{
		out << "\t" "{\n"
			"\tstd::auto_ptr< oess_1::stdsn::oextension_t > "
			" future_ext( s.extension_start() );\n"
			"\t" "future_ext->pack_opaque( "
				"o.oess_unknown_extension() );\n"
			"\t""s.extension_finish( *future_ext );\n"
			"\t" "}\n";
	}

	for( ext_level = 1;
		ext_level <= type.extension_deep();
		++ext_level )
	{
		std::string ext_variable_name( std::string( "e" ) +
			cpp_util_2::slexcast( ext_level ) );

		out << "\t} /* s( " << ext_variable_name << " ) */\n"
			"\t""s.extension_finish( *" << ext_variable_name << " );\n"
			"\t} /* " << ext_variable_name << " */\n";
	}
}

//
// escape_cpp_string
//

/*!
	\since v.1.2.0

	Выполняет escaping символов, которые не могут в явном
	виде присутствовать в C++ строке. Выполняются замены:
	- двойная кавычка заменяется на `\"';
	- обратный слеш заменяется на `//';
*/
std::string
escape_cpp_string( const std::string & what )
{
	std::string r( what );
	for( std::string::size_type i = 0; i != r.length(); ++i )
	{
		if( '\\' == r[ i ] )
		{
			r.replace( i, 1, "\\\\" );
			++i;
		}
		else if( '\"' == r[ i ] )
		{
			r.replace( i, 1, "\\\"" );
			++i;
		}
	}

	return r;
}

//
// generate_scheme_method
//
static void
generate_scheme_method(
	const type_t & type,
	std::ostream & out )
{
	std::ostringstream scheme_repres_stream;
	make_type_representation( type, scheme_repres_stream, false );
	std::string	scheme( escape_cpp_string(
		scheme_repres_stream.str() ) );

	out << "const std::string &\n"
		<< type.query_cpp_mapping().query_name()
		<< "::oess_serializer_t::scheme()\n"
		"{\n"
		"\tstatic std::string s(\n";

	std::string::size_type i = 0, l = scheme.length();
	while( i < l ) {
		std::string::size_type c = l - i;
		if( c > 48 )
			c = 48;
		out << "\t\t\t\"" << scheme.substr( i, c ) << "\"\n";
		i += c;
	}

	out << "\t\t);\n"
		"\treturn s;\n"
		"}\n";
}

//
// generate_oess_present_if
//

/*!
	\since v.1.2.0
	\brief Генерация кода метода oess_present_if.
*/
static void
generate_oess_present_if(
	const std::string & type_name,
	const pseudo_attr_list_t & attrs,
	std::ostream & out )
{
	try
	{
		out << "bool\n" << type_name
			<< "::oess_present_if( const std::string & a ) const\n"
			"{\n";
		std::for_each( attrs.begin(), attrs.end(),
			make_present_if_method_fragment_t( out ) );
		out << "\treturn false;\n" "}\n";
	}
	catch( const std::exception & x )
	{
		OESS_THROW_PHYSIC(
			oess_1::scheme::err::c_serialization_code_generation_fail,
			"C++ type: " << type_name << ", caused by: "
			<< x.what() )
	}
}

//
// generate_oess_set_default
//

/*!
	\since v.1.2.0
	\brief Генерация кода метода oess_set_default.
*/
static void
generate_oess_set_default(
	const std::string & type_name,
	const pseudo_attr_list_t & attrs,
	std::ostream & out )
{
	try
	{
		out << "void\n" << type_name
			<< "::oess_set_default( const std::string & a )\n"
			"{\n";
		std::for_each( attrs.begin(), attrs.end(),
			make_set_default_method_fragment_t( out ) );
		out << "}\n";
	}
	catch( const std::exception & x )
	{
		OESS_THROW_PHYSIC(
			oess_1::scheme::err::c_serialization_code_generation_fail,
			"C++ type: " << type_name << ", caused by: "
			<< x.what() )
	}
}

//
// generate_get_subclass_extension_path
//

/*!
	\since v.1.2.0
	\brief Генератор метода oess_get_subclass_extension_path.
*/
static void
generate_get_subclass_extension_path(
	//! Приемник сгенерированного кода.
	std::ostream & out,
	//! Обрабатываемый тип.
	const type_t & type,
	//! Имя обрабатываемого типа в C++.
	const std::string & cpp_type_name,
	//! Список базовых типов для обрабатываемого типа.
	const pseudo_base_list_t & bases )
	{
		out << "void" "\n" << cpp_type_name
			<< "::oess_get_subclass_extension_path(" "\n"
			"\t" "oess_1::stdsn::subclass_extension_path_t & path, " "\n"
			"\t" "const std::string & terminator ) const" "\n"
			"{" "\n";

		if( type.is_subclassing_by_extension() )
			{
				out << "\t" "if( oess_unknown_subclass_extension_path() )\n\t"
					"{\n\t\t"
						"path = *oess_unknown_subclass_extension_path();\n\t\t"
						"return;\n\t"
					"}\n";

				if( !type.is_abstract() )
					out << "\t" "path.push_back(" "\n"
						"\t\t" "oess_serializer_t::type_name()," "\n"
						"\t\t" "oess_serializer_t::scheme() );" "\n";
				out << "\t" "if( oess_serializer_t::type_name() "
					"!= terminator )" "\n";
				if( type.is_derived_as_extension() )
					{
						// Поскольку тип произведен как расширение,
						// то первый базовый и является нужным нам
						// базовым типом.
						const base_t & base = *( bases.begin() );
						out << "\t\t" << base.query_type().
							query_cpp_mapping().query_name()
							<< "::oess_get_subclass_extension_path( "
								"path, terminator );" "\n";
					}
				else
					{
						// Тип является вершиной иерархии.
						// Поэтому, если его имя не совпадает с
						// именем типа-терминатора, то это
						// исключительная ситуация.
						out << "\t" "OESS_THROW_PHYSIC(" "\n"
							"\t\t" "oess_1::stdsn::err::"
								"c_invalid_subclass_extension_chain," "\n"
							"\t\t" "oess_type_name() << \", terminator: \""
								"<< terminator )" "\n";
					}
			}
		else
			out << "\t" "OESS_THROW_PHYSIC(" "\n"
				"\t\t" "oess_1::stdsn::err::"
					"c_not_in_subclass_extension_chain," "\n"
				"\t\t" "oess_type_name() << \", terminator: \""
					"<< terminator )" "\n";

		out << "}" "\n";
	}

//
// generate_last_part
//
static void
generate_last_part(
	//! Приемник сгенерированного кода.
	std::ostream & out,
	//! Обрабатываемый тип.
	const type_t & type,
	//! Имя обрабатываемого типа в C++.
	const std::string & cpp_type_name,
	//! Список базовых типов для обрабатываемого типа.
	const pseudo_base_list_t & bases )
	{
		out << "void\n" << cpp_type_name << "::oess_unpack( "
			"oess_1::stdsn::ient_t & s )\n"
			"{\n\t"
				"oess_pre_unpack();\n\t"
				"oess_serializer_t::unpack( s, *this, 0 );\n\t"
				"oess_serializer_t::unpack_complete( s, *this );\n\t"
				"oess_post_unpack();\n"
			"}\n"
			"void\n" << cpp_type_name << "::oess_pack( "
			"oess_1::stdsn::oent_t & s ) const\n"
			"{\n\t"
				"oess_pre_pack();\n\t"
				"oess_serializer_t::pack( s, *this, 0 );\n\t"
				"oess_serializer_t::pack_complete( s, *this );\n\t"
				"oess_post_pack();\n"
			"}\n"
			"void *\n" << cpp_type_name << "::oess_cast( "
			"const std::string & cast_to )\n"
			"{\n\t"
				"return oess_serializer_t::cast( cast_to, this );\n"
			"}\n"
			"const std::string &\n" << cpp_type_name
			<< "::oess_type_name() const\n"
			"{\n\t"
				"return oess_serializer_t::type_name();\n"
			"}\n"
			"const std::string &\n" << cpp_type_name
			<< "::oess_scheme() const\n"
			"{\n\t"
				"return oess_serializer_t::scheme();\n"
			"}\n";

			if( type.is_extensible() )
				{
					out << "const oess_1::stdsn::unknown_extension_t &\n"
						<< cpp_type_name
						<< "::oess_unknown_extension() const\n"
						"{\n\t"
							"return oess_lo_unknown_extension( "
								"oess_serializer_t::type_name() );\n"
						"}\n"
						"oess_1::stdsn::unknown_extension_t &\n"
						<< cpp_type_name
						<< "::oess_writeable_unknown_extension()\n"
						"{\n\t"
							"return oess_lo_writeable_unknown_extension( "
								"oess_serializer_t::type_name() );\n"
						"}\n";
				}

			generate_get_subclass_extension_path(
				out, type, cpp_type_name, bases );
	}

//
// generate_unpack_self
//

/*!
	\since v.1.2.0
	\brief Генератор метода unpack_self для классов %oess_serializer_t.
*/
static void
generate_unpack_self(
	//! Поток для принятия сгенерированного кода.
	std::ostream & out,
	//! Тип для обработки.
	const type_t & type,
	//! Имя этого типа в C++.
	const std::string & cpp_type_name,
	//! Список атрибутов данного типа.
	const pseudo_attr_list_t & attrs )
{
	out << "void\n" << cpp_type_name << "::oess_serializer_t::unpack_self(\n\t"
			"oess_1::stdsn::ient_t & s,\n\t"
			<< cpp_type_name << " & o,\n\t"
			<< "oess_1::stdsn::isubclass_extension_t * subclass )\n"
		"{\n";
	std::for_each( attrs.begin(), attrs.end(),
		make_attr_missing_flags_t( out ) );
	generate_attr_unpack( type, out );
	std::for_each( attrs.begin(), attrs.end(),
		make_set_default_for_missing_attr_t( out ) );

	// Если тип поддерживает subclassing_by_extension, или
	// сам является подклассом-расширением, то оформим
	// содержимое производных классов в специальное расширение.
	if( type.is_subclassing_by_extension() )
	{
		out << "\n\t{\n\t\t"
			"std::auto_ptr< oess_1::stdsn::iextension_t >  "
				"derived_ext( s.extension_start() );\n\t\t"
			"if( subclass )\n\t\t\t"
				"(*subclass)( *derived_ext );\n\t\t"
			"else\n\t\t\t"
				"derived_ext->unpack_opaque( "
					"o.oess_writeable_unknown_subclass_extension() );"
					"\n\t\t"
			"s.extension_finish( *derived_ext );\n\t"
			"}\n";
	}

	out << "}\n";
}

//
// generate_unpack
//

/*!
	\since v.1.2.0
	\brief Генератор метода unpack для классов oess_serialize_t.
*/
static void
generate_unpack(
	//! Приемник сгенерированного кода.
	std::ostream & out,
	//! Тип, для которого выполняется генерация.
	const type_t & type,
	//! Имя этого типа в C++.
	const std::string & cpp_type_name,
	//! Список базовых типов для обрабатываемого типа.
	const pseudo_base_list_t & bases,
	//! Список атрибутор обрабатываемого типа.
	const pseudo_attr_list_t & attrs )
{
	out << "void\n" << cpp_type_name << "::oess_serializer_t::unpack(\n\t"
			"oess_1::stdsn::ient_t & s,\n\t"
			<< cpp_type_name << " & o,\n\t"
			"oess_1::stdsn::isubclass_extension_t * subclass )\n"
		"{\n\t"
			"if( s.obj_start( &o, &type_name(), scheme() ) )\n\t"
			"{\n\t\t"
				"oess_1::stdsn::isubclass_extension_t * "
						"our_subclass_ptr = 0;\n";

	// Если мы являемся подклассом-потомком, то нужно передать
	// в базовый класс объект для извлечения наших атрибутов.
	if( type.is_derived_as_extension() )
	{
		out << "\t\t"
				"oess_1::stdsn::isubclass_extension_template_t< "
			<< cpp_type_name << " > our_extension( o, &"
					<< cpp_type_name << "::oess_serializer_t::unpack_self, "
					"subclass );\n"
				"\t\t"
				"our_subclass_ptr = &our_extension;\n";
	}

	std::for_each( bases.begin(), bases.end(),
		make_unpack_for_base_t( out ) );

	if( !type.is_derived_as_extension() )
	{
		// Мы сами должны вызывать метод unpack_self.
		out << "\t\t"
				"unpack_self( s, o, subclass );\n";
	}

	out << "\t\t"
				"s.obj_finish( &o, &type_name() );\n\t"
			"}\n"
		"}\n";
}

//
// generate_unpack_complete
//

/*!
	\since v.1.2.0
	\brief Генератор метода unpack_complete для классов oess_serialize_t.
*/
static void
generate_unpack_complete(
	//! Приемник сгенерированного кода.
	std::ostream & out,
	//! Тип, для которого выполняется генерация.
	const type_t & type,
	//! Имя этого типа в C++.
	const std::string & cpp_type_name,
	//! Список базовых типов для обрабатываемого типа.
	const pseudo_base_list_t & bases )
{
	out << "void\n" << cpp_type_name
				<< "::oess_serializer_t::unpack_complete(\n\t"
			"oess_1::stdsn::ient_t & s,\n\t"
			<< cpp_type_name << " & o )\n"
		"{\n\t"
			"s.obj_complete( &o, &type_name() );\n";

	std::for_each( bases.begin(), bases.end(),
		make_unpack_complete_for_base_t( out ) );

	out << "}\n";
}

//
// generate_pack_self
//

/*!
	\since v.1.2.0
	\brief Генератор метода pack_self для классов %oess_serializer_t.
*/
static void
generate_pack_self(
	//! Приемник сгенерированного кода.
	std::ostream & out,
	//! Обрабатываемый тип.
	const type_t & type,
	//! Имя этого типа в С++.
	const std::string & cpp_type_name,
	//! Список атрибутов этого типа.
	const pseudo_attr_list_t & attrs )
{
	out << "void\n" << cpp_type_name << "::oess_serializer_t::pack_self(\n\t"
			"oess_1::stdsn::oent_t & s,\n\t"
			"const " << cpp_type_name << " & o,\n\t"
			"oess_1::stdsn::osubclass_extension_t * subclass )\n"
		"{\n";
	generate_attr_pack( type, out );

	// Если тип поддерживает subclassing_by_extension, или
	// сам является подклассом-расширением, то оформим
	// содержимое производных классов в специальное расширение.
	if( type.is_subclassing_by_extension() )
	{
		out << "\n\t{\n\t\t"
			"std::auto_ptr< oess_1::stdsn::oextension_t >  "
				"derived_ext( s.extension_start() );\n\t\t"
			"if( subclass )\n\t\t\t"
				"(*subclass)( *derived_ext );\n\t\t"
			"else\n\t\t\t"
				"derived_ext->pack_opaque( "
					"o.oess_unknown_subclass_extension() );"
					"\n\t\t"
			"s.extension_finish( *derived_ext );\n\t"
			"}\n";
	}

	out << "}\n";
}

//
// generate_pack
//

/*!
	\since v.1.2.0
	\brief Генератор метода pack для классов %oess_serializer_t.
*/
static void
generate_pack(
	//! Приемник сгенерированного кода.
	std::ostream & out,
	//! Обрабатываемый тип.
	const type_t & type,
	//! Имя обрабатываемого типа в C++.
	const std::string & cpp_type_name,
	//! Список базовых типов для обрабатываемого типа.
	const pseudo_base_list_t & bases,
	//! Список атрибутов обрабатываемого типа.
	const pseudo_attr_list_t & attrs )
{
	out << "void\n" << cpp_type_name << "::oess_serializer_t::pack(\n\t"
			"oess_1::stdsn::oent_t & s,\n\t"
			"const " << cpp_type_name << " & o,\n\t"
			"oess_1::stdsn::osubclass_extension_t * subclass )\n"
		"{\n\t"
			"if( s.obj_start( &o, &type_name(), scheme() ) )\n\t"
			"{\n\t\t"
				"oess_1::stdsn::osubclass_extension_t * "
					"our_subclass_ptr = 0;\n";

	// Если мы являемся подклассом-потомком, то нужно передать
	// в базовый класс объект для сохранения наших атрибутов.
	if( type.is_derived_as_extension() )
	{
		out << "\t\t"
				"oess_1::stdsn::osubclass_extension_template_t< "
			<< cpp_type_name << " > our_extension( o, &"
					<< cpp_type_name << "::oess_serializer_t::pack_self, "
					"subclass );\n"
				"\t\t"
				"our_subclass_ptr = &our_extension;\n";
	}

	std::for_each( bases.begin(), bases.end(),
		make_pack_for_base_t( out ) );

	if( !type.is_derived_as_extension() )
	{
		// Мы сами должны вызывать метод unpack_self.
		out << "\t\t"
				"pack_self( s, o, subclass );\n";
	}

	out << "\t\t"
				"s.obj_finish( &o, &type_name() );\n\t"
			"}\n"
		"}\n";
}

//
// generate_pack_complete
//

/*!
	\since v.1.2.0
	\brief Генератор метода pack_complete для классов %oess_serializer_t.
*/
static void
generate_pack_complete(
	//! Приемник сгенерированного кода.
	std::ostream & out,
	//! Обрабатываемый тип.
	const type_t & type,
	//! Имя обрабатываемого типа в C++.
	const std::string & cpp_type_name,
	//! Список базовых типов для обрабатываемого типа.
	const pseudo_base_list_t & bases )
{
	out << "void\n" << cpp_type_name
				<< "::oess_serializer_t::pack_complete(\n\t"
			"oess_1::stdsn::oent_t & s,\n\t"
			"const " << cpp_type_name << " & o )\n"
		"{\n\t"
			"s.obj_complete( &o, &type_name() );\n";

	std::for_each( bases.begin(), bases.end(),
		make_pack_complete_for_base_t( out ) );

	out << "}\n";
}

//
// generate_cpp_serializer
//

OESS_1__SCHEME__FUNC(void)
generate_cpp_serializer(
	const type_t & type,
	std::ostream & out )
{
	const std::string & cpp_type_name =
		type.query_cpp_mapping().query_name();

	pseudo_base_list_t bases( type );
	pseudo_attr_list_t attrs( type );

	generate_first_part( cpp_type_name, out );

	// Генерируем методы сериализации/десериализации.
	generate_unpack_self( out, type, cpp_type_name, attrs );
	generate_unpack( out, type, cpp_type_name, bases, attrs );
	generate_unpack_complete( out, type, cpp_type_name, bases );
	generate_pack_self( out, type, cpp_type_name, attrs );
	generate_pack( out, type, cpp_type_name, bases, attrs );
	generate_pack_complete( out, type, cpp_type_name, bases );

	// Генерируем метод oess_serializer_t::cast
	out << "void *\n" << cpp_type_name << "::oess_serializer_t::cast(\n\t"
			"const std::string & cast_to,\n\t"
			<< cpp_type_name << " * o )\n"
		"{\n"
			"\tif( cast_to == type_name() )\n"
			"\t\treturn o;\n"
			"\tvoid * p = 0;\n";
	std::for_each( bases.begin(), bases.end(),
		make_cast_for_base_t( out ) );
	out << "\tif( cast_to == oess_1::stdsn::serializable_t::"
		"oess_serializable_t_name() )\n"
		"\t\treturn (oess_1::stdsn::serializable_t *)o;\n"
		"\treturn p;\n"
		"}\n";

	// Генерируем метод oess_serializer_t::type_name
	out << "const std::string &\t" << cpp_type_name
		<< "::oess_serializer_t::type_name() {\n"
		"\tstatic std::string name( \"" << type.query_name() << "\" );\n"
		"\treturn name;\n"
		"}\n";
	// Генерируем метод oess_serializer_t::factory
	out << "oess_1::stdsn::serializable_t *\t" << cpp_type_name
		<< "::oess_serializer_t::factory() {\n";
	if( type.is_abstract() )
		out << "\treturn 0;\n}\n";
	else
		out << "\treturn new " << cpp_type_name << "();\n}\n";
	// Генерируем метод oess_serializer_t::scheme
	generate_scheme_method( type, out );

	out << "oess_1::stdsn::factory_registrator_t\n" << cpp_type_name
		<< "::oess_serializer_t::m_factory_reg( \""
		<< type.query_name() << "\", &"
		<< cpp_type_name << "::oess_serializer_t::factory );\n";

	generate_oess_present_if( cpp_type_name, attrs, out );
	generate_oess_set_default( cpp_type_name, attrs, out );

	generate_last_part( out, type, cpp_type_name, bases );
}

} /* namespace impl */

} /* namespace scheme */

} /* namespace oess_1 */

