/*

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
	\brief Интерфейсы средств сериализации и десериализации сущностей.
*/

#if !defined( _OESS_1__STDSN__ENT_HPP_ )
#define _OESS_1__STDSN__ENT_HPP_

#include <cpp_util_2/h/nocopy.hpp>

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

#include <oess_1/io/h/stream.hpp>

namespace oess_1 {

namespace stdsn {

class	iextension_t;
class	oextension_t;

class	unknown_extension_t;

//
// ient_t
//

/*!
	\brief Базовый класс для управления разметкой сущности
	во входном потоке.
*/
class	OESS_1__STDSN__TYPE	ient_t
:	private cpp_util_2::nocopy_t
{
	public :
		virtual ~ient_t();

		/*!
			Вызывается перед началом десериализации объекта.

			\return true, если объект должен быть десериализован. false,
				если эта составляющая объекта из-за виртуального
				множественного наследования уже была десериализована
				и повторную десериализацию осуществлять не нужно.
		*/
		virtual bool
		obj_start(
			const void * object,
			const std::string * type_name,
			const std::string & scheme ) = 0;

		/*!
			Вызывается после окончания десериализации атрибутов
			объекта и его базовых типов.
		*/
		virtual void
		obj_finish(
			const void * object,
			const std::string * type_name ) = 0;

		/*!
			Вызывается после полного окончания десериализации объекта.
			При множественном наследовании между обращениями к
			obj_finish и obj_complete для одного и того же объекта еще
			может осуществляться десериализация составляющих,
			унаследованных из других базовых типов.
		*/
		virtual void
		obj_complete(
			const void * object,
			const std::string * type_name ) = 0;

		//! Распаковка имени типа из входного потока.
		/*!
			Имя типа сохраняется в потоке при сохранении
			объекта по указателю на базовый класс.
			В различных потоках могут применяться различные
			способы представления подобных имен (в виде строки,
			идентификатора типа и т.д.).
		*/
		virtual void
		unpack_type_name(
			//! Приемник распакованого имени типа.
			std::string & type_name ) = 0;

		//! Получение доступа к входному потоку.
		virtual oess_1::io::istream_t &
		query_stream() = 0;

		/*!
			\since v.1.2.0
			\brief Проверить метку наличия сериализованного
				опционального атрибута.
			\return \a true, если опциональный атрибут сериализован
				и может быть извлечен из потока. \a false в противном
				случае.
		*/
		virtual bool
		check_optional_attr_existence(
			//! Имя опционального атрибута.
			const std::string & attr_name ) = 0;

		/*!
			\since v.1.2.0
			\brief Создать объект, управляющий разметкой расширения
				сущности.

			Должен вызываться при начале обработки расширения
			сериализованного объекта.

			\return Динамически созданный объект, который должен
				быть удален посредством delete.
		*/
		virtual iextension_t *
		extension_start() = 0;

		/*!
			\since v.1.2.0
			\brief Обработка завершения десериализации расширения
				объекта.
		*/
		virtual void
		extension_finish(
			//! Объект, который был получен предшествующим
			//! вызовом extension_start().
			iextension_t & extension ) = 0;
};

//
// iextension_t
//

/*!
	\since v.1.2.0
	\brief Базовый класс для управления разметкой расширения
		объекта во входном потоке.
*/
class	OESS_1__STDSN__TYPE	iextension_t
:	public ient_t
{
	public :
		virtual ~iextension_t();

	//! \name Унаследованные методы.
	//! \{
		virtual bool
		obj_start(
			const void * object,
			const std::string * type_name,
			const std::string & scheme ) = 0;

		virtual void
		obj_finish(
			const void * object,
			const std::string * type_name ) = 0;

		virtual void
		obj_complete(
			const void * object,
			const std::string * type_name ) = 0;

		virtual void
		unpack_type_name(
			std::string & type_name ) = 0;

		virtual oess_1::io::istream_t &
		query_stream() = 0;

		virtual bool
		check_optional_attr_existence(
			const std::string & attr_name ) = 0;

		virtual iextension_t *
		extension_start() = 0;

		virtual void
		extension_finish(
			iextension_t & extension ) = 0;
	//! \}

		/*!
			\brief Есть ли в раширении данные.
			\return \a true, если расширение не пусто и из него
				можно извлекать значения атрибутов.
		*/
		virtual bool
		is_present() const = 0;

		/*!
			\brief Поместить все содержимое расширения в виде
				двоичного образа в указанное хранилище.
		*/
		virtual void
		unpack_opaque( unknown_extension_t & ext ) = 0;
};

//
// oent_t
//

/*!
	\brief Базовый класс для управления разметкой сущности
	в выходном потоке.
*/
class	OESS_1__STDSN__TYPE	oent_t
:	private cpp_util_2::nocopy_t
{
	public :
		virtual ~oent_t();

		/*!
			Вызывается перед началом сериализации объекта.

			\return true, если объект должен быть сериализован. false,
				если эта составляющая объекта из-за виртуального
				множественного наследования уже была сериализована
				и повторную сериализацию осуществлять не нужно.
		*/
		virtual bool
		obj_start(
			//! Указатель на сериализуемый объект.
			const void * object,
			//! Этот указатель должен оставаться корректным до вызова
			//! метода obj_finish.
			const std::string * type_name,
			const std::string & scheme ) = 0;

		//! Помещение в поток маркера окончания объекта.
		virtual void
		obj_finish(
			//! Указатель на сериализуемый объект. Тот же самый указатель,
			//! который использовался при вызове метода obj_start.
			const void * object,
			//! Тот же самый указатель, который использовался в
			//! методе obj_start.
			const std::string * type_name ) = 0;

		/*!
			Вызывается после полного окончания сериализации объекта.
			При множественном наследовании между обращениями к
			obj_finish и obj_complete для одного и того же объекта еще
			может осуществляться сериализация составляющих,
			унаследованных из других базовых типов.
		*/
		virtual void
		obj_complete(
			const void * object,
			const std::string * type_name ) = 0;

		//! Упаковка имени типа в выходного поток.
		/*!
			Имя типа сохраняется в потоке при сохранении
			объекта по указателю на базовый класс.
			В различных потоках могут применяться различные
			способы представления подобных имен (в виде строки,
			идентификатора типа и т.д.).

			\par v.1.1.0
			Добавлен аргумент type_scheme.
		*/
		virtual void
		pack_type_name(
			//! Имя типа сохраняемого объекта.
			const std::string & type_name,
			//! Описание типе сохраняемого объекта.
			const std::string & type_scheme ) = 0;

		//! Получение доступа к выходному потоку.
		virtual oess_1::io::ostream_t &
		query_stream() = 0;

		/*!
			\since v.1.2.0
			\brief Установить метку наличия сериализованного
				опционального атрибута.
			\return значение параметра \a existence.
		*/
		virtual bool
		set_optional_attr_existence(
			//! Имя опционального атрибута.
			const std::string & attr_name,
			//! Будет ли атрибут сериализован.
			//! \a true, если будет, \a false в противном случае.
			bool existence ) = 0;

		/*!
			\since v.1.2.0
			\brief Создать объект, управляющий разметкой расширения
				сущности.

			Должен вызываться при начале сериализации расширения
			объекта.

			\return Динамически созданный объект, который должен
				быть удален посредством delete.
		*/
		virtual oextension_t *
		extension_start() = 0;

		/*!
			\since v.1.2.0
			\brief Обработка завершения сериализации расширения
				объекта.
		*/
		virtual void
		extension_finish(
			//! Объект, который был получен предшествующим
			//! вызовом extension_start().
			oextension_t & extension ) = 0;
};

//
// oextension_t
//

/*!
	\since v.1.2.0
	\brief Базовый класс для управления разметкой расширения сущности
	в выходном потоке.
*/
class	OESS_1__STDSN__TYPE	oextension_t
:	public oent_t
{
	public :
		virtual ~oextension_t();

	//! \name Реализация унаследованных методов.
	//! \{
		virtual bool
		obj_start(
			const void * object,
			const std::string * type_name,
			const std::string & scheme ) = 0;

		virtual void
		obj_finish(
			const void * object,
			const std::string * type_name ) = 0;

		virtual void
		obj_complete(
			const void * object,
			const std::string * type_name ) = 0;

		virtual void
		pack_type_name(
			const std::string & type_name,
			const std::string & type_scheme ) = 0;

		virtual oess_1::io::ostream_t &
		query_stream() = 0;

		virtual bool
		set_optional_attr_existence(
			const std::string & attr_name,
			bool existence ) = 0;

		virtual oextension_t *
		extension_start() = 0;

		virtual void
		extension_finish(
			oextension_t & extension ) = 0;
	//! \}

		/*!
			\brief Упаковать расширение в указанный поток.

			Предполагается, что атрибуты, входящие в расширение, сначала
			упаковываются во временный вспомогательный поток. После
			того как все атрибуты расширения будут сериализованы,
			содержимое временного потока должно быть перенесено в общий
			поток объекта. Для этого у расширения предназначен
			данный метод.
		*/
		virtual void
		pack( oess_1::io::ostream_t & to ) const = 0;

		/*!
			\brief Упаковать двоичный образ неизвестного расширения.

			Предполагается, что атрибуты, входящие в расширение, сначала
			упаковываются во временный вспомогательный поток. Если же
			атрибутов в расширении нет, а есть только двоичный образ
			неизвестного расширения, то в этот временный вспомогательный
			поток должен быть упакован двоичный образ неизвестного
			расширения. Именно для этой упаковки предназначен данный
			метод.
		*/
		virtual void
		pack_opaque( const unknown_extension_t & ext ) = 0;
};

//
// isubclass_extension_t
//

/*!
	\since v.1.2.0
	\brief Интерфейс десериализатора расширения производного
		класса для механизма subclassing_by_extension.
*/
class	OESS_1__STDSN__TYPE	isubclass_extension_t
	{
		public :
			virtual ~isubclass_extension_t();

			//! Метод для десериализации расширения производного
			//! класса из потока.
			virtual void
			operator()( ient_t & ) = 0;
	};

//
// osubclass_extension_t
//

/*!
	\since v.1.2.0
	\brief Интерфейс сериализатора расширения производного
		класса для механизма subclassing_by_extension.
*/
class	OESS_1__STDSN__TYPE	osubclass_extension_t
	{
		public :
			virtual ~osubclass_extension_t();

			//! Метод для сериализации расширения производного
			//! класса из потока.
			virtual void
			operator()( oent_t & ) = 0;
	};

//
// isubclass_extension_template_t
//

/*!
	\since v.1.2.0
	\brief Реализация интерфейса %isubclass_extension_t в виде
		шаблона.
*/
template< class T >
class	isubclass_extension_template_t
:	public isubclass_extension_t
	{
		public :
			//! Псевдоним для типа десериализуемого объекта.
			typedef T object_t;
			//! Указатель на функцию, которая выполняет десериализацию.
			typedef void (*unpack_self_t)(
				ient_t &, T &, isubclass_extension_t * );

			//! Инициализирующий конструктор.
			isubclass_extension_template_t(
				//! Десериализуемый объект.
				object_t & o,
				//! Функция, которая реально выполняет десериализацию.
				unpack_self_t unpack_self,
				//! Десериализатор для производного класса.
				//! Может быть нулем, если производного класса нет.
				isubclass_extension_t * subclass )
				:	m_object( o )
				,	m_unpack_self( unpack_self )
				,	m_subclass( subclass )
				{}

			virtual void
			operator()( ient_t & from )
				{
					(*m_unpack_self)( from, m_object, m_subclass );
				}

		private :
			//! Десериализуемый объект.
			object_t &	m_object;
			//! Функция, которая реально выполняет десериализацию.
			unpack_self_t	m_unpack_self;
			//! Десериализатор для производного класса.
			/*!
				Значение 0 означает, что производного класса нет.
			*/
			isubclass_extension_t *	m_subclass;
	};

//
// osubclass_extension_template_t
//

/*!
	\since v.1.2.0
	\brief Реализация интерфейса %osubclass_extension_t в виде
		шаблона.
*/
template< class T >
class	osubclass_extension_template_t
:	public osubclass_extension_t
	{
		public :
			//! Псевдоним для типа сериализуемого объекта.
			typedef T object_t;
			//! Указатель на функцию, которая выполняет сериализацию.
			typedef void (*pack_self_t)(
				oent_t &, const T &, osubclass_extension_t * );

			//! Инициализирующий конструктор.
			osubclass_extension_template_t(
				//! Сериализуемый объект.
				const object_t & o,
				//! Функция, которая реально выполняет сериализацию.
				pack_self_t pack_self,
				//! Сериализатор для производного класса.
				//! Может быть нулем, если производного класса нет.
				osubclass_extension_t * subclass )
				:	m_object( o )
				,	m_pack_self( pack_self )
				,	m_subclass( subclass )
				{}

			virtual void
			operator()( oent_t & from )
				{
					(*m_pack_self)( from, m_object, m_subclass );
				}

		private :
			//! Сериализуемый объект.
			const object_t &	m_object;
			//! Функция, которая реально выполняет сериализацию.
			pack_self_t	m_pack_self;
			//! Сериализатор для производного класса.
			/*!
				Значение 0 означает, что производного класса нет.
			*/
			osubclass_extension_t *	m_subclass;
	};


} /* namespace stdsn */

} /* namespace oess_1 */

#endif
