/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CXmlTokenizer.h  -  description
                             -------------------
    begin                : Sun May 22 2003
    based on the code    : Mikko Lahteenmaki <Laza@Flashmail.com>
    copyright            : (C) 2003-2005 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>

#define BUF_SIZE 4096

/// @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;
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
