/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CXmlTokenizer.h  -  description
                             -------------------
    begin                : Sun May 22 2003
    based on the code    : Mikko Lahteenmaki <Laza@Flashmail.com>
    copyright            : (C) 2003-2006 by Alexey S.Parshin
    email                : alexeyp@gmail.com
 ***************************************************************************/

/***************************************************************************
   This library is free software; you can redistribute it and/or modify it 
   under the terms of the GNU Library General Public License as published by
   the Free Software Foundation; either version 2 of the License, or (at 
   your option) any later version.

   This library 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. See the GNU Library
   General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; if not, write to the Free Software Foundation, 
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

   Please report all bugs and problems to "alexeyp@gmail.com"
 ***************************************************************************/

#ifndef __CXMLTOKENIZER_H__
#define __CXMLTOKENIZER_H__

#include <stdio.h> 
#include <stack>
#include <string>

namespace sptk {

   /// @addtogroup XML
   /// @{

   /// @brief XML locator (internal).
   ///
   /// CXmlLocator class provides the XML handler with information about 
   /// the position in a file. 
	class SP_EXPORT CXmlLocator {
		friend class CXmlTokenizer;

#define BUF_SIZE 4096 ///< Buffer size

	public:
      /// Constructor
		CXmlLocator() { m_line=m_col=1; }

      /// Returns line number, where error happened.
		int line() const { return m_line; }

      /// Returns column in line, where error happened.
		int col() const { return m_col; }

      /// Returns line in file, where error happened.
		static std::string errorLine(const char *filename, const CXmlLocator &locator);

	private:
		int   m_line;  ///< Line number
		int   m_col;   ///< Column number
	};

   /// @brief XML tokenizer (internal)
   ///
   /// Base class for tokenizing through XML stream.
	class CXmlTokenizer {
		friend class CXmlParser;
	public:

      /// Constructor
		CXmlTokenizer();

      /// Destructor
		virtual ~CXmlTokenizer() { }

      /// Returns current stream locator.
		CXmlLocator *locator() const { return m_locator; }

   /**
    * Set current stream locator.
    * @param locator to set
    */
		void locator(CXmlLocator *locator) { m_locator = locator; }

	protected:
    /**
    * Derived tokenizers override this function.
    * Returns true, if END-OF-STREAM
    */
		virtual bool stream_eos() const = 0;

    /**
    * Derived tokenizers override this function.
    * Tries to 'length' data from stream to 'buf' buffer.
    * Returns bytes readed.
    * @param buf buffer to read
    * @param length to read from stream to buffer
    */
		virtual int stream_read(char *buf, int length) = 0;

	private:
      /// End of stream indicator
		bool eos() const { return stream_eos() && read_buf_len<=0; }

      /// Sets ignore parsing whitespaces and new-line characters mode
		void cdata_mode(bool val) { cdata_mode_ = val; }

      /// Returns true if parsing whitespaces and new-line characters is ignored
		bool cdata_mode() const { return cdata_mode_ || auto_cdata_; } 

      /// Sets parsing prolog mode
		void prolog_mode(bool val) { prolog_mode_ = val; }

      /// Returns true is parsing prolog mode is on
		bool prolog_mode() const { return prolog_mode_; }

      /// Sets attribute mode
		void attr_mode(bool val) { attr_mode_ = val; }

      /// Returns attribute mode
		bool attr_mode() const { return attr_mode_; }

      /// Sets parsing preformatted text mode
		void pre_mode(bool val) { pre_mode_ = val; }

      /// Returns parsing preformatted text mode
		bool pre_mode() const { return pre_mode_; }

      /// Dereference operator
		const std::string &operator*() const { return curtoken; }

      /// Reads next token in stream
		CXmlTokenizer &operator++()    { read_next(); return *this; }

      /// Reads next token in stream
		CXmlTokenizer &operator++(int) { read_next(); return *this; }

      /// Puts the token back into the stream
		void put_back(const std::string &token) { putback_stack.push(token); }

      /// Puts the token back into the stream
		void put_back(const char *token) { putback_stack.push(token); }

      /// Puts the token back into the stream
		void put_back() { put_back(curtoken); }

      /// Gets one character from internal buffer
		char read_char();

      /// Gets next token, stores it to 'curtoken'
		void read_next();

      /// Reads buffer
		void fill_buffer();

		char  read_buf[BUF_SIZE];  ///< Read buffer
		int   read_buf_len;        ///< Read buffer len
		int   read_buf_pos;        ///< Read buffer position

		char write_buf[BUF_SIZE];  ///< Write buffer
		int write_buf_pos;         ///< Write buffer position

      /// Current parsed token
		std::string curtoken;

      /// Char which was put back internally
		char putback_char;

      /// Stack for put_back()'ed tokens
		std::stack<std::string> putback_stack;

      /// Internally used to recognize chars in the stream
		bool is_literal( char c ) const;

      /// Internally used to recognize chars in the stream
		bool is_whitespace( char c ) const;

      /// Internally used to recognize chars in the stream
		bool is_newline( char c ) const;

      /// Internally used to recognize chars in the stream
		bool is_delimiter( char c ) const; // start-/endchar of a string

		bool cdata_mode_;          ///< Cdata-mode doesn't care for whitespaces in strings
		bool  auto_cdata_;         ///< Auto cdata-mode turned on internally, doesnt add new-lines
		bool prolog_mode_;         ///< Prolog parsing mode flag
		bool  attr_mode_;          ///< Attribute mode flag
		bool pre_mode_;            ///< Preformated text parsing flag

		CXmlLocator *m_locator;    ///< Locator
	};

   /// @brief XML default tokenizer (internal)
   ///
   /// Simple XML tokenizer implementation.
	class CXmlDefaultTokenizer : public CXmlTokenizer {
	public:
      /// Contructor with inputbuffer 
      /// @param buffer CBuffer *, buffer with XML data
		CXmlDefaultTokenizer(const CBuffer& buffer);

      /// Destructor
		virtual ~CXmlDefaultTokenizer() {}

	protected:
      /// Reads length bytes from stream, removed from stream
      /// @param buf char *, output buffer
      /// @param length int, the read length
		virtual int stream_read(char *buf, int length);

      /// Returns true at the end of stream
		virtual bool stream_eos() const;

	private:
      /// Current read position in the buffer
		unsigned m_bufferPosition;
      /// The external XML data buffer pointer
		const CBuffer *m_buffer;
	};

   /// Returns true if the str is literal
	inline bool is_literal(const std::string &str, bool cdata=false) {
		if (str.length()!=1) return false;
		switch (str[0]) {
			case '/': case '?': 
			case '=': case '!':
			case '>':
				if (cdata) return false;
			case '<':
				return true;
		}
		return false;
	}

   /// Returns the literal type
	inline char literal(const std::string &str) { return str[0]; }

   /// @}
}
#endif
