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

#ifndef XSD_CXX_PARSER_EXPAT_ELEMENTS_HXX
#define XSD_CXX_PARSER_EXPAT_ELEMENTS_HXX

#include <string>
#include <iosfwd>

#include <expat.h>

#include <xsd/cxx/xml/elements.hxx>
#include <xsd/cxx/xml/error-handler.hxx>

#include <xsd/cxx/parser/exceptions.hxx>
#include <xsd/cxx/parser/elements.hxx>

#include <xsd/cxx/parser/expat/transcoder.hxx>

namespace xsd
{
  namespace cxx
  {
    namespace parser
    {
      namespace expat
      {

        // Simple auto pointer for Expat's XML_Parser object.
        //
        struct parser_auto_ptr
        {
          ~parser_auto_ptr ()
          {
            if (parser_ != 0)
              XML_ParserFree (parser_);
          }

          parser_auto_ptr (XML_Parser parser)
              : parser_ (parser)
          {
          }

        public:
          operator XML_Parser ()
          {
            return parser_;
          }

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

        private:
          XML_Parser parser_;
        };

        //
        //
        template <typename C>
        struct event_router
        {
          typedef std::basic_string<C> string;
          typedef expat::transcoder<XML_Char, C> transcoder;


        public:
          event_router (traverser_base<C>& t);

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

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

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

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

          void
          end_element_ (XML_Char const* ns_name);

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

        private:
          void
          split_name (XML_Char const* s, string& ns, string& name);

        private:
          XML_Parser parser_;

          traverser_base<C>& t_;
          string buf_;
        };


        //
        //
        template <typename C>
        struct context
        {
          ~context ()
          {
            clear ();
          }

          // This copy c-tor is for internal use. Context objects
          // should not be otherwise copied. @@ Need to use auto_ptr-
          // like ref mechanism.
          //
          context (context const& other)
              : doc_ (other.doc_),
                router_ (doc_),
                parser_ (other.parser_)
          {
            other.clear ();
            set ();
          }

        private:
          context (XML_Parser parser,
                   traverser_impl<C>& traverser,
                   std::basic_string<C> const& root_namespace,
                   std::basic_string<C> const& root_name)
              : doc_ (traverser, root_namespace, root_name),
                router_ (doc_),
                parser_ (parser)
          {
            set ();
          }

        private:
          void
          set ()
          {
            XML_SetUserData(parser_, &router_);

            XML_SetStartElementHandler (
              parser_, event_router<C>::start_element);
            XML_SetEndElementHandler (
              parser_, event_router<C>::end_element);
            XML_SetCharacterDataHandler (
              parser_, event_router<C>::characters);
          }

          void
          clear () const
          {
            XML_SetUserData(parser_, 0);

            XML_SetStartElementHandler (parser_, 0);
            XML_SetEndElementHandler (parser_, 0);
            XML_SetCharacterDataHandler (parser_, 0);
          }

        private:
          document<C> doc_;
          event_router<C> router_;
          XML_Parser parser_;

        private:
          template <typename>
          friend class traverser_common;

          template <typename, typename>
          friend class traverser;
        };


        // Common code before generic and void-specialization split.
        //
        template <typename C>
        struct traverser_common: virtual traverser_impl<C>
        {
        protected:
          void
          _parse_ (std::basic_string<C> const& file,
                   std::basic_string<C> const& root_element_namespace,
                   std::basic_string<C> const& root_element_name);

          void
          _parse_ (std::basic_string<C> const& file,
                   std::basic_string<C> const& root_element_namespace,
                   std::basic_string<C> const& root_element_name,
                   xml::error_handler<C>&);

        protected:
          void
          _parse_ (std::istream&,
                   std::basic_string<C> const& root_element_namespace,
                   std::basic_string<C> const& root_element_name);

          void
          _parse_ (std::istream&,
                   std::basic_string<C> const& root_element_namespace,
                   std::basic_string<C> const& root_element_name,
                   xml::error_handler<C>&);

          void
          _parse_ (std::istream&,
                   std::basic_string<C> const& system_id,
                   std::basic_string<C> const& root_element_namespace,
                   std::basic_string<C> const& root_element_name);

          void
          _parse_ (std::istream&,
                   std::basic_string<C> const& system_id,
                   std::basic_string<C> const& root_element_namespace,
                   std::basic_string<C> const& root_element_name,
                   xml::error_handler<C>&);

          void
          _parse_ (std::istream&,
                   std::basic_string<C> const& system_id,
                   std::basic_string<C> const& public_id,
                   std::basic_string<C> const& root_element_namespace,
                   std::basic_string<C> const& root_element_name);

          void
          _parse_ (std::istream&,
                   std::basic_string<C> const& system_id,
                   std::basic_string<C> const& public_id,
                   std::basic_string<C> const& root_element_namespace,
                   std::basic_string<C> const& root_element_name,
                   xml::error_handler<C>&);

        private:
          bool
          _parse_ (std::istream&,
                   std::basic_string<C> const* system_id,
                   std::basic_string<C> const* public_id,
                   std::basic_string<C> const& root_element_namespace,
                   std::basic_string<C> const& root_element_name,
                   xml::error_handler<C>&);

        protected:
          context<C>
          _parse_begin_ (XML_Parser,
                         std::basic_string<C> const& root_element_namespace,
                         std::basic_string<C> const& root_element_name);

          void
          _parse_end_ (context<C>&);
        };


        //
        //
        template <typename X, typename C>
        struct traverser: virtual traverser_common<C>
        {
          virtual X
          post () = 0;

        public:
          // Parse a local file. The file is accessed with std::ifstream
          // in binary mode. std::ios_base::failure exception is used to
          // report io errors (badbit and failbit).
          X
          _parse (std::basic_string<C> const& file,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name);

          // Parse a local file with a user-provided error_handler
          // object. The file is accessed with std::ifstream in binary
          // mode. std::ios_base::failure exception is used to report
          // io errors (badbit and failbit).
          //
          X
          _parse (std::basic_string<C> const& file,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name,
                  xml::error_handler<C>&);

        public:
          // System id is a "system" identifier of the resources (e.g.,
          // URI or a full file path). Public id is a "public" identifier
          // of the resource (e.g., application-specific name or relative
          // file path). System id is used to resolve relative paths.
          // In diagnostics messages system id is used if public id is
          // not available. Otherwise public id is used.
          //
          // Note that these sematics are open for interpretation. Both
          // system and public ids are optional and you can view them as
          // a way to associate two strings with the stream being parsed
          // which are made available to you in error handlers.
          //


          // Parse std::istream.
          //
          X
          _parse (std::istream&,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name);

          // Parse std::istream with user-provided error_handler object.
          //
          X
          _parse (std::istream&,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name,
                  xml::error_handler<C>&);

          // Parse std::istream with a system id.
          //
          X
          _parse (std::istream&,
                  std::basic_string<C> const& system_id,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name);

          // Parse std::istream with a system id and a user-provided
          // error_handler object.
          //
          X
          _parse (std::istream&,
                  std::basic_string<C> const& system_id,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name,
                  xml::error_handler<C>&);

          // Parse std::istream with system and public ids.
          //
          X
          _parse (std::istream&,
                  std::basic_string<C> const& system_id,
                  std::basic_string<C> const& public_id,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name);

          // Parse std::istream with system and public ids and a user-provided
          // error_handler object.
          //
          X
          _parse (std::istream&,
                  std::basic_string<C> const& system_id,
                  std::basic_string<C> const& public_id,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name,
                  xml::error_handler<C>&);

        public:
          // Low-level Expat-specific parsing API. A typical use case
          // would look like this (pseudo-code):
          //
          // XML_Parser xml_parser (XML_ParserCreateNS (0, ' '));
          // context ctx (parser._parse_begin (xml_parser, "...", "..."));
          //
          // while (more_stuff_to_parse)
          // {
          //    // Call XML_Parse or XML_ParseBuffer
          //    // Handle errors if any.
          // }
          //
          // result_type result (parser._parse_end (ctx));
          //
          // Notes:
          //
          // 1. Context object shall not be copied (except as a return
          //    value from _parse_begin.
          //
          // 2. 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 (second
          //    argument to XML_ParserCreateNS).
          //
          context<C>
          _parse_begin (XML_Parser,
                        std::basic_string<C> const& root_element_namespace,
                        std::basic_string<C> const& root_element_name);

          X
          _parse_end (context<C>&);
        };


        // Specialization for void.
        //
        template <typename C>
        struct traverser<void, C>: virtual traverser_common<C>
        {
          virtual void
          post ()
          {
          }

        public:
          void
          _parse (std::basic_string<C> const& file,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name);

          void
          _parse (std::basic_string<C> const& file,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name,
                  xml::error_handler<C>&);

        public:
          void
          _parse (std::istream&,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name);

          void
          _parse (std::istream&,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name,
                  xml::error_handler<C>&);

          void
          _parse (std::istream&,
                  std::basic_string<C> const& system_id,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name);

          void
          _parse (std::istream&,
                  std::basic_string<C> const& system_id,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name,
                  xml::error_handler<C>&);

          void
          _parse (std::istream&,
                  std::basic_string<C> const& system_id,
                  std::basic_string<C> const& public_id,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name);

          void
          _parse (std::istream&,
                  std::basic_string<C> const& system_id,
                  std::basic_string<C> const& public_id,
                  std::basic_string<C> const& root_element_namespace,
                  std::basic_string<C> const& root_element_name,
                  xml::error_handler<C>&);

        public:
          context<C>
          _parse_begin (XML_Parser,
                        std::basic_string<C> const& root_element_namespace,
                        std::basic_string<C> const& root_element_name);

          void
          _parse_end (context<C>&);
        };
      }
    }
  }
}

#include <xsd/cxx/parser/expat/elements.txx>

#endif  // XSD_CXX_PARSER_EXPAT_ELEMENTS_HXX
