/*

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.

*/

/*!
	\file oess_1/stdsn/h/serializable.hpp
	\brief Интерфейс %oess_1::stdsn::serializable_t.
*/

#if !defined( _OESS_1__STDSN__SERIALIZABLE_HPP_ )
#define _OESS_1__STDSN__SERIALIZABLE_HPP_

#include <list>

#include <cpp_util_2/h/nocopy.hpp>

#include <auto_ptr_3/h/obj_ptr.hpp>

#include <oess_1/stdsn/h/declspec.hpp>

#include <oess_1/stdsn/h/ent.hpp>
#include <oess_1/stdsn/h/macro.hpp>

namespace oess_1 {

namespace stdsn {

//
// unknown_extension_t
//

/*!
	\since v.1.2.0
	\brief Хранилище неизвестного расширения.

	Подробнее см. \ref oess_1_2_0__unknown_extension.
*/
class	OESS_1__STDSN__TYPE	unknown_extension_t
	{
		private :
			//! Двоичное значение расширения.
			std::string	m_data;

		public :
			//! Конструктор по-умолчанию.
			unknown_extension_t();
			//! Инициализирующий конструктор.
			explicit unknown_extension_t(
				const std::string & data );

			//! Получить значение расширения.
			const std::string &
			get() const;

			//! Сбросить значение расширения.
			/*!
				После вызова этого метода будет считаться, что
				расширение пусто (т.е. отсутствует).
			*/
			void
			clear();
	};

//
// all_unknown_extension_t
//

struct	all_unknown_extensions_data_t;

/*!
	\since v.1.2.0
	\brief Хранилище всех неизвестных расширений объекта.

	Все расширения простых расширяемых (помечаемых в
	DDL как {extensible}) типов храняться в базовом типе
	serializable_t. Для хранения всех расширений, ассоциированных
	с именами типов, которым расширения принадлежат, предназначен
	данный тип.

	Подробнее см. \ref oess_1_2_0__unknown_extension.
*/
class	OESS_1__STDSN__TYPE	all_unknown_extensions_t
	{
		public :
			//! Конструктор по-умолчанию.
			all_unknown_extensions_t();
			//! Конструктор копирования.
			all_unknown_extensions_t(
				const all_unknown_extensions_t & o );
			~all_unknown_extensions_t();

			//! Оператор копирования.
			all_unknown_extensions_t &
			operator=(
				const all_unknown_extensions_t & o );

			//! Получить расширение для указанного типа.
			/*!
				\return расширение этого типа, если оно было определено.
					В противном случае -- ссылку на пустое расширение.
			*/
			const unknown_extension_t &
			get( const std::string & type_name ) const;

			//! Получить объект для сохранения расширения указанного типа.
			/*!
				\return объект для сохранения расширения.
			*/
			unknown_extension_t &
			get( const std::string & type_name );

			//! Удалить все расширения.
			void
			drop();

		private :
			//! Реальное содержимое объекта.
			auto_ptr_3::obj_ptr_t< all_unknown_extensions_data_t >	m_data;
	};

//
// subclass_extension_path_t
//

/*!
	\since v.1.2.0
	\brief Класс для сохранения элементов в цепочке наследования
		при использовании механизма subclassing_by_extension.

	Цепочку наследования необходимо сохранять при сериализации
	для того, чтобы при десериализации создать объект ближайщего
	базового типа к типу сериализованного объекта.

	Первым элементом цепочки является имя реально сериализованного
	типа. Последним элементом (терминатором) является имя типа,
	которое было указано в DDL описании в {attr {of {extension_of}}}.

	Подробнее см. \ref oess_1_2_0__subclassing_by_extension.
*/
class	OESS_1__STDSN__TYPE
subclass_extension_path_t
	{
		public :
			/*!
				\since v.1.2.0
				\brief Элемент цепочки наследования.
			*/
			struct OESS_1__STDSN__TYPE
			item_t
				{
					//! Имя типа.
					std::string	m_type_name;
					//! Схема данных типа.
					std::string	m_scheme;

					//! Конструктор по-умолчанию.
					item_t();
					//! Инициализирующий конструктор.
					item_t(
						const std::string & type_name,
						const std::string & scheme );
					//! Конструктор копирования.
					item_t(
						const item_t & o );

					//! Оператор копирования.
					item_t &
					operator=( const item_t & o );
				};

			//! Псевдоним для списка элементов в цепочке.
			typedef std::list< item_t >	list_t;
			//! Псевдоним для константного итератора по цепочке.
			typedef list_t::const_iterator	const_iterator;

			//! Конструктор по-умолчанию.
			subclass_extension_path_t();
			//! Конструктор копирования.
			subclass_extension_path_t(
				const subclass_extension_path_t & o );
			~subclass_extension_path_t();

			//! Оператор копирования.
			subclass_extension_path_t &
			operator=(
				const subclass_extension_path_t & o );

			//! Поместить еще один элемент в конец цепочки.
			void
			push_back(
				//! Имя типа.
				const std::string & type_name,
				//! Схема данных типа.
				const std::string & type_scheme );

			//! Количество элементов в цепочке.
			oess_1::uint_t
			size() const;

			//! Получить итератор на начало цепочки.
			const_iterator
			begin() const;

			//! Получить итератор на конец цепочки.
			/*!
				\return Итератор на несуществующий элемент, расположенный
					сразу за последним элементом цепочки.
			*/
			const_iterator
			end() const;

			/*!
				\brief Оператор сравнения.

				Сравниваются только имена типов.
			*/
			bool
			operator==( const subclass_extension_path_t & o ) const;

		private :
			//! Цепочка наследования.
			list_t	m_list;
	};

//
// serializable_t
//

/*!
	\brief Интерфейс, который должны наследовать все
	сериализуемые типы.
*/
class	OESS_1__STDSN__TYPE	serializable_t
	{
		// То, что скрывается за макросом OESS_SERIALIZER
		// в базовом классе должно быть реализовано явно.
		public :
			/*!
				\brief Вспомогательный класс для сериализации.
				\if oess_full_api
					Содержит не все методы, определяемые в
					макросе OESS_SERIALIZER(), т.к. класс
					serializable_t является абстрактным не
					некоторые методы (pack, unpack, cast,...)
					просто не имеют смысла.
				\endif
			*/
			class	OESS_1__STDSN__TYPE	oess_serializer_t
				{
					public :
						static const std::string &	type_name();
						/*!
							\if oess_full_api
								Возвращает 0, т.к. serializable_t
								является абстрактным классом.
							\endif
						*/
						static oess_1::stdsn::serializable_t *	factory();
						static const std::string &	scheme();
				};
			friend class	oess_serializer_t;

		public :
			/*!
				\since v.1.2.0
				\brief Конструктор по-умолчанию.
			*/
			serializable_t();
			/*!
				\since v.1.2.0
				\brief Конструктор копирования.

				Необходим для копирования неизвестных расширений.
			*/
			serializable_t(
				const serializable_t & o );
			virtual ~serializable_t();

			/*!
				\since v.1.2.0
				\brief Оператор копирования.

				Необходим для копирования неизвестных расширений.
			*/
			serializable_t &
			operator=( const serializable_t & o );

			//! Запись объекта в выходной поток.
			virtual void
			oess_pack( oent_t & writter ) const = 0;

			//! Чтение объекта из входного потока.
			virtual void
			oess_unpack( ient_t & reader ) = 0;

			//! Преобразование к указаному типу.
			/*!
				Если преобразование не возможно возвращается 0.
			*/
			virtual void *
			oess_cast( const std::string & type_name ) = 0;

			//! Имя типа, к которому реально принадлежит объект.
			virtual const std::string &
			oess_type_name() const = 0;

			//! DDL-описание типа.
			/*! \since v.1.1.0 */
			virtual const std::string &
			oess_scheme() const = 0;

			/*!
				\since v.1.2.0
				\brief Заполнить цепочку наследования для механизма
					subclassing_by_extension.

				\throw oess_1::ex_t, если тип не участвует в механизме
					subclassing_by_extension или если указан не верный
					тип-терминатор.
			*/
			virtual void
			oess_get_subclass_extension_path(
				oess_1::stdsn::subclass_extension_path_t & path,
				const std::string & terminator ) const = 0;

			//! Имя типа serializable_t.
			/*!
				\return Значение "oess_1::stdsn::serializable_t".
			*/
			static std::string &
			oess_serializable_t_name();

		protected :
			//! Этот метод вызывается из метода oess_pack перед
			//! началом сериализации объекта.
			/*!
				Предназначен для выполнения произвольных действий
				перед сериализацией объекта.
			*/
			virtual void
			oess_pre_pack() const;

			//! Этот метод вызывается из метода oess_pack после
			//! сериализации объекта.
			/*!
				Предназначен для выполнения произвольных действий
				после сериализации объекта.

				\since v.1.0.3
			*/
			virtual void
			oess_post_pack() const;

			//! Этот метод вызывается из метода oess_unpack
			//! перед выполнением десериализации объекта.
			/*!
				Предназначен для выполнения произвольных действий
				перед десериализацией объекта.

				После десериализации объект будет содержать новое
				значение. Данный метод позволяет осуществить
				очистку ресурсов, связанных с предыдущим
				значением объекта.

				\since v.1.0.3
			*/
			virtual void
			oess_pre_unpack();

			//! Этот метод вызывается из метода oess_unpack
			//! после выполнения десериализации объекта.
			/*!
				Предназначен для выполнения произвольных действий
				после десериализации объекта.
			*/
			virtual void
			oess_post_unpack();

		//! \name Поддержка неизвестных расширений.
		//! \{
			public :
				/*!
					\since v.1.2.0
					\brief Сохранить в объекте цепочку наследования,
						извлеченную при десериализации.

					Сохраненный таким образом объект-цепочка будет
					копироваться оператором копирования.

					Сохраненный объект цепочка будет сохраняться до
					тех пор, пока не будет вызван метод
					oess_drop_unknown_extensions().

					См. \ref oess_1_2_0__subclassing_by_extension,
						\ref oess_1_2_0__unknown_extension.
				*/
				void
				oess_store_unknown_subclass_extension_path(
					const subclass_extension_path_t & path );

				/*!
					\since v.1.2.0
					\brief Получить доступ к извлеченной при десериализации
						цепочки наследования.

					\return 0, если:
						- не было десериализации;
						- при десериализации цепочка наследования совпала
						с цепочкой наследования, которую десериализованный
						объект производит методом oess_get_subclass_extension_path;
						- был вызван метод oess_drop_unknown_extensions();
						- объект не использует механизма subclassing_by_extension.

					См. \ref oess_1_2_0__subclassing_by_extension,
						\ref oess_1_2_0__unknown_extension.
				*/
				const subclass_extension_path_t *
				oess_unknown_subclass_extension_path() const;

				/*!
					\since v.1.2.0
					\brief Сбросить все неизвестные расширения.

					Сбрасываются все неизвестные расширения, относящиеся
					как к простым расширяемым типам, так и к механизму
					subclassing_by_extension. В случае механизма
					subclassing_by_extension сбрасывается так же и
					сохраненная при десериализации цепочка наследования
					(если была сохранена).

					См. \ref oess_1_2_0__extensible_types,
						\ref oess_1_2_0__subclassing_by_extension,
						\ref oess_1_2_0__unknown_extension.
				*/
				void
				oess_drop_unknown_extensions();

			protected :
				/*!
					\since v.1.2.0
					\brief Получить доступ к неизвестному расширению в механизме
						subclassing_by_extension.

					См. \ref oess_1_2_0__subclassing_by_extension,
						\ref oess_1_2_0__unknown_extension.
				*/
				const unknown_extension_t &
				oess_unknown_subclass_extension() const;

				/*!
					\since v.1.2.0
					\brief Получить доступ к неизвестному расширению в механизме
						subclassing_by_extension.

					См. \ref oess_1_2_0__subclassing_by_extension,
						\ref oess_1_2_0__unknown_extension.
				*/
				unknown_extension_t &
				oess_writeable_unknown_subclass_extension();

				/*!
					\since v.1.2.0
					\brief Получить доступ к неизвестному расширению типа.

					См. \ref oess_1_2_0__unknown_extension.
				*/
				const unknown_extension_t &
				oess_lo_unknown_extension(
					const std::string & type_name ) const;

				/*!
					\since v.1.2.0
					\brief Получить доступ к неизвестному расширению типа
						для изменения расширения.

					См. \ref oess_1_2_0__unknown_extension.
				*/
				unknown_extension_t &
				oess_lo_writeable_unknown_extension(
					const std::string & type_name );

			private :
				/*!
					\since v.1.2.0
					\brief Значение цепочки наследования, извлеченной
						при десериализации в механизме subclassing_by_extension.

					Содержит указатель только если была операция десериализации
					и десериализованная цепочка оказалась не совпадающей с
					той цепочкой, которая возвращается полученным при
					десериализации объектом. Т.е. в случае, когда был
					десериализован неизвестный объект.
				*/
				auto_ptr_3::obj_ptr_t<
						subclass_extension_path_t >
					m_oess_unknown_subclass_extension_path;

				/*!
					\since v.1.2.0
					\brief Значение неизвестного расширения в механизме
						subclassing_by_extension.
				*/
				unknown_extension_t	m_oess_unknown_subclass_extension;
				/*!
					\since v.1.2.0
					\brief Значение всех неизвестных расширений типов в
						иерархии наследования.
				*/
				all_unknown_extensions_t	m_oess_unknown_extensions;
		//! \}
	};

OESS_1__STDSN__FUNC( oent_t & )
operator<<( oent_t & writter, const serializable_t & o );

OESS_1__STDSN__FUNC( ient_t & )
operator>>( ient_t & reader, serializable_t & o );

//
// pfn_factory
//
typedef serializable_t * (*pfn_factory_t)();

//
// factory_registrator_t
//

/*!
	\brief Внутренний класс ObjESSty.

	\if oess_full_api

	Класс, регистрирующий функцию создания объекта
	в глобальной фабрике объектов.

	\else

	Объявлен в открытом заголовочном файле из-за того,
	что этот тип используется в генерируемом коде
	сериализации.

	\endif
*/
class	OESS_1__STDSN__TYPE	factory_registrator_t {
	public :
		factory_registrator_t(
			const char * type_name,
			pfn_factory_t factory );
		~factory_registrator_t();

	private :
		std::string	m_type_name;
		pfn_factory_t	m_factory;
};

//
// obj_factory_t
//

/*!
	\brief Внутренний класс ObjESSty.

	Глобальная фабрика объектов.
*/
class	OESS_1__STDSN__TYPE	obj_factory_t {
	public :
		/*!
			\since v.1.2.0
			\brief Действие, которое нужно выполнить, если
				фабрика для объекта не найдена.
		*/
		enum no_factory_action_t
		{
			//! Необходимо породить исключение.
			throw_exception,
			//! Вместо порождения исключение необходимо
			//! возвратить нулевой указатель.
			no_throw_exception
		};

		static void
		reg(
			const std::string & type_name,
			pfn_factory_t factory );

		static void
		dereg(
			const std::string & type_name,
			pfn_factory_t factory );

		/*!
			\brief Создать объект по имени типа.

			Если создать
			объект не удается, то порождается исключение.
			Проверяется возможность приведения созданного
			объекта к указателю на указанный тип.
			Если приведение типа не возможно, то
			порождается исключение.

			\par v.1.2.0
			Если фабрика для объекта не найдена и
			\a no_factory_action == no_throw_exception, то
			вместо порождения исключения возвращается
			нулевой указатель.
		*/
		static serializable_t *
		create(
			//! Имя типа, который нужно создать.
			const std::string & type_to_create,
			//! Имя типа, к которому нужно привести указатель
			//! на созданный объект.
			const std::string & type_to_cast,
			//! Что нужно делать, если фабрика не найдена.
			const no_factory_action_t & no_factory_action =
				throw_exception );
};

} /* namespace stdsn */

} /* namespace oess_1 */

#endif

