// file      : xsde/cxx/parser/expat/document.hxx
// author    : Boris Kolpackov <boris@codesynthesis.com>
// copyright : Copyright (c) 2005-2007 Code Synthesis Tools CC
// license   : GNU GPL v2 + exceptions; see accompanying LICENSE file

#ifndef XSDE_CXX_PARSER_EXPAT_DOCUMENT_HXX
#define XSDE_CXX_PARSER_EXPAT_DOCUMENT_HXX

#include <xsde/cxx/config.hxx>

#include <stddef.h> // size_t

#ifdef XSDE_STL
#  include <string>
#endif

#ifdef XSDE_IOSTREAM
#  include <iosfwd>
#endif

#include <xsde/c/expat/expat.h>

// We only support UTF-8 expat for now.
//
#ifdef XML_UNICODE
#error UTF-16 expat (XML_UNICODE defined) is not supported
#endif

#include <xsde/cxx/string.hxx>

#include <xsde/cxx/parser/elements.hxx>

#ifndef XSDE_EXCEPTIONS
#  include <xsde/cxx/parser/error.hxx>
#endif

namespace xsde
{
  namespace cxx
  {
    namespace parser
    {
      namespace expat
      {
        // Simple auto pointer for Expat's XML_Parser object.
        //
        struct parser_auto_ptr
        {
          ~parser_auto_ptr ();

          explicit
          parser_auto_ptr (XML_Parser = 0);

          parser_auto_ptr&
          operator= (XML_Parser);

        public:
          operator XML_Parser ()
          {
            return parser_;
          }

        private:
          parser_auto_ptr (const parser_auto_ptr&);

          parser_auto_ptr&
          operator= (const parser_auto_ptr&);

        private:
          XML_Parser parser_;
        };

        //
        //
        class document_pimpl
        {
        public:
	  virtual
          ~document_pimpl ();

          document_pimpl (parser_base&,
                          const char* root_element_name);

          document_pimpl (parser_base&,
                          const char* root_element_namespace,
                          const char* root_element_name);

#ifdef XSDE_STL
          document_pimpl (parser_base&,
                          const std::string& root_element_name);

          document_pimpl (parser_base&,
                          const std::string& root_element_namespace,
                          const std::string& root_element_name);
#endif

        protected:
          document_pimpl ();

          // This function is called to obtain the root element type parser.
          // If the returned pointed is 0 then the whole document content
          // is ignored.
          //
          virtual parser_base*
          start_root_element (const ro_string& ns, const ro_string& name);

          // This function is called to indicate the completion of document
          // parsing. The parser argument contains the pointer returned by
          // start_root_element.
          //
          virtual void
          end_root_element (const ro_string& ns,
                            const ro_string& name,
                            parser_base* parser);

#ifdef XSDE_IOSTREAM
        public:
          // Parse a local file. The file is accessed with std::ifstream
          // in binary mode. The std::ios_base::failure exception is used
          // to report io errors (badbit and failbit) if XSDE_EXCEPTIONS
          // is defined. Otherwise error codes are used.
          //
          void
          parse (const char* file);

#ifdef XSDE_STL
          void
          parse (const std::string& file);
#endif

          // Parse std::istream. std::ios_base::failure exception is used
          // to report io errors (badbit and failbit) if XSDE_EXCEPTIONS
          // is defined. Otherwise error codes are used.
          //
          void
          parse (std::istream&);
#endif

        public:
          // Parse a chunk of input. You can call this function multiple
          // times with the last call having the last argument true.
          //
          void
          parse (const void* data, size_t size, bool last);

        public:
          // Low-level Expat-specific parsing API. A typical use case
          // would look like this (pseudo-code):
          //
          // XML_Parser xml_parser (XML_ParserCreateNS (0, ' '));
          //
          // xxx_pimpl root;
          // document_pimpl doc (root, "root");
          //
          // root.pre ();
          // doc.parse_begin (xml_parser);
          //
          // while (more_stuff_to_parse)
          // {
          //    // Call XML_Parse or XML_ParseBuffer:
          //    //
          //    if (XML_Parse (...) != XML_STATUS_ERROR)
          //      break;
          // }
          //
          // doc.parse_end ();
          // result_type result (root.post_xxx ());
          //
          // Notes:
          //
          // 1. If your XML instances use XML namespaces, XML_ParserCreateNS
          //    functions should be used to create the XML parser. Space
          //    (XML_Char (' ')) should be used as a separator (the second
          //    argument to XML_ParserCreateNS).
          //
          // 2. If XML_Parse or XML_ParseBuffer fail, call parse_end to
          //    determine the error which is indicated either via exception
          //    or set as an error code.
          //
          void
          parse_begin (XML_Parser);

          void
          parse_end ();

#ifndef XSDE_EXCEPTIONS
        public:
          const error&
          _error () const;
#endif

        protected:
          void
          set ();

          void
          clear ();

        protected:
          static void XMLCALL
          start_element (void*, const XML_Char*, const XML_Char**);

          static void XMLCALL
          end_element (void*, const XML_Char*);

          static void XMLCALL
          characters (void*, const XML_Char*, int);

        protected:
          void
          start_element_ (const XML_Char* ns_name, const XML_Char** atts);

          void
          end_element_ (const XML_Char* ns_name);

          void
          characters_ (const XML_Char* s, size_t n);

        protected:
          XML_Parser xml_parser_;
          parser_auto_ptr auto_xml_parser_;

          parser_base* parser_;

          string root_name_;
          string root_ns_;

          size_t depth_;

#ifndef XSDE_EXCEPTIONS
          error error_;
#endif
        };
      }
    }
  }
}

#include <xsde/cxx/parser/expat/document.ixx>

#endif  // XSDE_CXX_PARSER_EXPAT_DOCUMENT_HXX
