/*

cls_2: Curl Like Syntax
Yauheni A. Akhotnikau (C) 2001-2003
eao197@yahoo.com
-------------------------------------------------

Permission is granted to anyone to use this software for any purpose on any
computer system, and to redistribute it freely, subject to the following
restrictions:

1. This software is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

2. The origin of this software must not be misrepresented, either by
   explicit claim or by omission.

3. Altered versions must be plainly marked as such, and must not be
   misrepresented as being the original software.

-------------------------------------------------

*/

/*!
	\file
	\brief Основной заголовочный файл CLS.
*/

#if !defined( _CLS_2__CLS_HPP_ )
#define _CLS_2__CLS_HPP_

#include <auto_ptr_3/h/obj_ptr.hpp>
#include <auto_ptr_3/h/vect_ptr.hpp>

namespace cls_2 {

class	tag_t;
class	parser_t;

//
// Return codes
//

//! \name Коды ошибок.
//! \{

//! Нет ошибок.
const int	c_ok = 0;
//! Синтаксическая ошибка.
const int	c_syntax_error = 1;
//! Семантическая ошибка.
/*!
	Ошибка, связанная с неправильными или недопустимым использованием
	тегов и их значений.
*/
const int	c_semantic_error = 2;
//! Ошибка преждевременного достижения конца входного потока.
const int	c_eof = 3;
//! Ошибка ввода вывода.
const int	c_io_error = 4;
//! Лексическая ошибка.
/*!
	Один из вариантов синтаксической ошибки, вызыванный невозможностью
	корректного разбора какого-либо из токенов.
*/
const int	c_lex_error = 5;
//! Отсутствие входного потока при попытке проведения разбора.
const int	c_no_input_stream = 6;

//! \}

//
// Messages ids
//

const int	c_msg_undefined_mandatory_tag = 1;
	// Default: <tag-name> " : undefined mandatory tag\n"

const int	c_msg_unfinished_back_slash_seq = 2;
	// Default: "unfinished back slash sequence\n"

const int	c_msg_invalid_back_slash_seq = 3;
	// Default: "invalid back slash sequence\n"

const int	c_msg_unclosed_comment = 4;
	// Default: "unclosed comment\n"

const int	c_msg_tag_expected = 5;
	// Default: "tag expected\n"

const int	c_msg_syntax_error = 6;
	// Default: "syntax error\n"

const int	c_msg_tag_name_expected = 7;
	// Default: "tag name expected\n"

const int	c_msg_fill_tag_name_error = 8;
	// Default: "space or } expected after tag name\n"

const int	c_msg_enclosed_string = 9;
	// Default: "unclosed string\n";

const int	c_msg_unexpected_eof = 10;
	// Default: "unexpected EOF\n";

const int	c_msg_missmatched_close_block_count = 11;
	// Default: "missmatched count of '}'\n"

const int	c_msg_unknown_tag = 12;
	// Default: <tag-name> " : unknown tag\n";

const int	c_msg_tag_already_defined = 13;
	// Default: <tag-name> " : tag already defined\n";

const int	c_msg_tag_value_must_be_specified = 14;
	// Default: <tag-name> " : tag value must be specified\n";

const int	c_msg_invalid_value = 15;
	// Default: <value> " : invalid or disallowed value\n";

const int	c_msg_unexpected_tok_string = 16;
	// Default: "unexpected 'STRING' token\n";

const int	c_msg_unexpected_tok_nonspace = 17;
	// Default: "unexpected 'NONSPACE' token\n";

const int	c_msg_value_out_of_range = 18;
	// Default: <value> " : out of range\n";

//
// input_stream_t
//

//! Базовый класс для входного потока.
class	input_stream_t {
	public :
		input_stream_t();
		virtual ~input_stream_t();

		enum ret_code_t {
			//! Все нормально.
			e_ok = 0,
			//! Достигнут конец входного потока.
			e_eof = 1,
			//! Обнаружена ошибка, не позволяющая
			//! осуществлять последующее чтение.
			e_error = 2
		};

		//! Взять следующий символ из входного потока.
		/*!
			\return элемент перечисления ret_code_t.
		*/
		virtual int
		get( char & ch ) = 0;

		//! Возвратить последний извлеченный символ обратно
		//! во входной поток.
		/*!
			\return элемент перечисления ret_code_t.
		*/
		virtual void
		back() = 0;

		//! Текущий номер строки.
		/*!
			\since v.2.6
		*/
		virtual unsigned int
		query_line() const = 0;

	private :
		input_stream_t(
			const input_stream_t & );
		input_stream_t &
		operator=(
			const input_stream_t & );
};

//
// error_stream_t
//

/*
	Base class for error stream
*/
class	error_stream_t {
	public :
		error_stream_t();
		virtual ~error_stream_t();

		virtual error_stream_t &
		out(
			// May be NULL
			const char * sz ) = 0;

	private :
		error_stream_t(
			const error_stream_t & );
		error_stream_t &
		operator=(
			const error_stream_t & );
};

//
// msg_storage_t
//

/*
	Base class for error message storage
*/
class	msg_storage_t {
	public :
		msg_storage_t();
		virtual ~msg_storage_t();

		// Searches message by message id.
		// Returns 0 if message not found.
		virtual const char *
		find( int msg_id ) const = 0;

	private :
		msg_storage_t(
			const msg_storage_t & );
		msg_storage_t &
		operator=(
			const msg_storage_t & );
};

//
// tag_formatter_t
//

/*
	Base class for formatting tags on output
*/
class	tag_formatter_t {
	public :
		virtual ~tag_formatter_t();

		// Called on start of tag. May be called multiple
		// times on children tags.
		virtual void
		start_tag( const char * tag_name ) = 0;

		// Called on start of tag. May be called multiple
		// times on children tags.
		virtual void
		finish_tag() = 0;

		// Called for formatting value (value can be SPACE,
		// NONSPACE or STRING token). May be called multiple
		// times inside one tag
		virtual void
		out_value( const char * value ) = 0;
};

//
// parser_context_t
//

//! Контекст, на котором должен производиться разбор.
/*!
	Для разбора CLS-содержимого должен быть определен
	входной поток, из которого извлекается CLS-содержимое.
	Так же может быть определен поток ошибок, в который
	должна помещаться информация об обнаруженных ошибках.
	Для описания найденых ошибок может потребоваться
	хранилище сообщений об ошибках.
	
	Вся эта информация является контекстом парсера. И описывается
	контекст парсера при помощи данного класса.

	\attention Контекст парсера только хранит указатели на
	объекты входного потока, потока ошибок и хранилища сообщений.
	Но не управляет временем жизни этих объектов.
*/
class	parser_context_t
{
	public :
		//! Конструктор по-умолчанию.
		parser_context_t();
		//! Конструктор копирования.
		parser_context_t(
			const parser_context_t & o );
		~parser_context_t();

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

		//! Установить новый входной поток.
		/*!
			\return предыдущий входной поток.
		*/
		input_stream_t *
		set_input_stream(
			//! Может быть 0. В этом случае будет считаться,
			//! что контексту новый входной поток не назначен.
			input_stream_t * stream );
		//! Получить текущий входной поток.
		input_stream_t *
		query_input_stream() const;

		//! Установить новый поток ошибок.
		/*!
			\return предыдущий входной поток.
		*/
		error_stream_t *
		set_error_stream(
			//! Может быть 0. В этом случае будет считаться,
			//! что контексту новый поток ошибок не назначен.
			error_stream_t * stream );
		//! Получить текущий поток ошибок.
		error_stream_t *
		query_error_stream() const;

		//! Отображает указанную строку на поток ошибок.
		/*!
			Ничего не делает, если поток ошибок не назначен.
		*/
		void
		show_error(
			//! Может быть 0.
			const char * str );

		//! Установить новый хранилище сообщений.
		/*!
			\return предыдущее хранилище сообщений.
		*/
		msg_storage_t *
		set_msg_storage(
			//! Может быть 0. В этом случае будет считаться,
			//! что контексту новый хранилище сообщений
			//! не назначено.
			msg_storage_t * storage );
		//! Получить текущее хранилище ошибок.
		msg_storage_t *
		query_msg_storage() const;

		//! Поиск сообщения в текущем хранилище ошибок.
		/*!
			Поиск осуществляется только если хранилище
			ошибок задано.
			
			\return 0, если сообщение не найдено или не
			задано хранилище ошибок.
		*/
		const char *
		find_msg( int msg_id ) const;

	protected :
		//! Текущий входной поток.
		input_stream_t *	m_input_stream;
		//! Текущий поток ошибок.
		error_stream_t *	m_error_stream;
		//! Текущее хранилище сообщений.
		msg_storage_t *	m_msg_storage;
};

//
// tag_t
//

/*!
	\brief Base class for tag
*/
class	tag_t {
	public :
		//! Стандартный конструктор.
		tag_t(
			//! Cannot be null!
			const char * name );
		//! Конструктор для случая, когда тег является
		//! подчиненным тегом и атрибутом в другом объекте теге.
		/*!
			\since v.2.6.2

			Выполняет: owner.tag_add( self_tag() );
			Т.е. прописывает себя подчиненным тегом указанного
			тега владельца.
		*/
		tag_t(
			//! Тег-владелец.
			tag_t & owner,
			//! Не должен быть 0!.
			const char * name );
		virtual ~tag_t();

		//! Возвращает ссылку на самого себя.
		/*! \since v.2.6.2

			Предназначен для использования в конструкторе
			производных типов при инициализации подчиненных
			тегов - атрибутов. */
		tag_t &
		self_tag()
		{
			return *this;
		}

		//! Возвращает ссылку на самого себя.
		/*! \since v.2.6.2

			Предназначен для использования в конструкторе
			производных типов при инициализации подчиненных
			тегов - атрибутов. */
		const tag_t &
		self_tag() const
		{
			return *this;
		}

		//! Called when tag parsing started.
		/*! Default implementation returns c_ok */
		virtual int
		on_start( parser_context_t & context );

		// Called when tag parsing finished.
		// Default implementation checks definitions
		// of all mandatory children tags.
		virtual int
		on_finish( parser_context_t & context );

		// Called when child tag parsing finished.
		// Default implementation returns c_ok
		virtual int
		on_tag(
			parser_context_t & context,
			tag_t & tag );

		// Must returns vector of pointers to the
		// children tags. Last item of vector must be 0.
		// NULL may be returned if there are no
		// children tags.
		// Default implementation returns current
		// list of children tags. Returned pointer may be
		// invalidated after list changing
		virtual tag_t **
		query_tags();

		// Returns name of tag.
		virtual const char *
		query_name() const;

		// Compare name of tag.
		// Default implementation simply compares
		// name and query_name()
		virtual bool
		compare_name( const char * name ) const;

		// Is this tag mandatory?
		// Default: no (false)
		virtual bool
		is_mandatory() const;

		// Is this tag defined (successfully parsed)?
		// Default: no (false)
		virtual bool
		is_defined() const;

		// Must resets object to its initial state
		// (as if object has not been parser at all).
		// Default implementation calls 'reset' for
		// all children tags
		virtual void
		reset();

		// Called for processing 'SPACE' token
		// Default implementation returns c_ok
		virtual int
		on_tok_space(
			parser_context_t & context,
			const char * tok_value );

		// Called for processing 'NONSPACE' token
		// Default implementation returns c_ok
		virtual int
		on_tok_nonspace(
			parser_context_t & context,
			const char * tok_value );

		// Called for processing 'STRING' token
		// Default implementation returns c_ok
		virtual int
		on_tok_string(
			parser_context_t & context,
			const char * tok_value );

		// Called for formatting tag on output.
		// Default implementation checks is_defined() and,
		// if tag defined, calls fmt.start_tag( query_name() ),
		// calls on_format virtual method, then calls format
		// for every children tag, then calls fmt.finish_tag.
		virtual void
		format(
			tag_formatter_t & fmt );

		// Adds tag to the end of children tags list
		void
		tag_add( tag_t & tag );

		// Removes first occurence of tag in the children tags list
		void
		tag_remove( tag_t & tag );

	protected :
		auto_ptr_3::vect_ptr_t< char >	m_name;

		typedef tag_t * tag_ptr_t;
		auto_ptr_3::vect_ptr_t< tag_ptr_t >	m_children_tags;
		size_t	m_children_tags_count;

		// Called from format(fmt) to output values of tag.
		// Empty method in this class
		virtual void
		on_format(
			tag_formatter_t & fmt );

	private :
		tag_t( const tag_t & );
		tag_t &
		operator=( const tag_t );

		//! Инициализация объекта.
		/*! \since v.2.6.2 */
		void
		init(
			//! Имя тега. Не должно быть 0!
			const char * name );
};

//
// parser_t
//
class	_parser_t;

/*
	Parser class
*/
class	parser_t {
	public :
		parser_t();
		~parser_t();

		int
		parse( parser_context_t & context );

		void
		tag_add( tag_t & tag );

		void
		tag_remove( tag_t & tag );

		size_t
		tag_stack_size() const;

		// Tag at top of stack has greater index than
		// tag at bottom of stack.
		const tag_t *
		tag_stack_at( size_t index ) const;

	protected :
		auto_ptr_3::obj_ptr_t< _parser_t >	m_parser;

	private :
		parser_t( const parser_t & );
		parser_t &
		operator=( const parser_t & );
};

} /* namespace cls_2 */

#endif
