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

#ifndef XSD_CXX_PARSER_ELEMENTS_HXX
#define XSD_CXX_PARSER_ELEMENTS_HXX

#include <memory>  // std::auto_ptr
#include <string>
#include <iosfwd>

#include <xercesc/sax/InputSource.hpp>
#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/DefaultHandler.hpp>

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

namespace xsd
{
  namespace cxx
  {
    namespace parser
    {
      template <typename C>
      struct traverser_base
      {
        virtual
        ~traverser_base ()
        {
        }

        virtual void
        start_element (std::basic_string<C> const&, // namespace
                       std::basic_string<C> const&) // name
        {
        }

        virtual void
        end_element (std::basic_string<C> const&, // namespace
                     std::basic_string<C> const&) // name
        {
        }

        virtual void
        attribute (std::basic_string<C> const&, // namespace
                   std::basic_string<C> const&, // name
                   std::basic_string<C> const&) // value
        {
        }

        virtual void
        characters (std::basic_string<C> const&)
        {
        }
      };


      //
      //
      template <typename C>
      struct traverser_impl: traverser_base<C>
      {
        traverser_impl ()
            : count_ (0), cur_ (0)
        {
        }

        // It's nice to have pre() here. This way we can use
        // one parser in the implementation of the other.
        //
        virtual void
        pre ()
        {
        }

        //
        //
        //
        virtual void
        start_element (std::basic_string<C> const& ns,
                       std::basic_string<C> const& name)
        {
          if (cur_)
            cur_->start_element (ns, name);
        }

        virtual void
        end_element (std::basic_string<C> const& ns,
                     std::basic_string<C> const& name)
        {
          if (cur_)
            cur_->end_element (ns, name);
        }

        virtual void
        attribute (std::basic_string<C> const& ns,
                   std::basic_string<C> const& name,
                   std::basic_string<C> const& value)
        {
          if (cur_)
            cur_->attribute (ns, name, value);
        }

        virtual void
        characters (std::basic_string<C> const& s)
        {
          if (cur_)
            cur_->characters (s);
        }

      protected:
        virtual bool
        start_element_ (std::basic_string<C> const& ns,
                        std::basic_string<C> const& n,
                        traverser_base<C>* cur);

        virtual bool
        end_element_ (std::basic_string<C> const& ns,
                      std::basic_string<C> const& n);

      protected:
        unsigned long count_;
        traverser_base<C>* cur_;
      };


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


      public:

        // Parse URI or a local file.
        //
        X
        parse (std::basic_string<C> const& uri,
               std::basic_string<C> const& root_element_name);

        X
        parse (C const* uri,
               std::basic_string<C> const& root_element_name);


        // Parse URI or a local file with a user-provided ErrorHandler object.
        // Note that you must initialize the Xerces-C++ runtime before calling
        // these functions.
        //
        X
        parse (std::basic_string<C> const& uri,
               std::basic_string<C> const& root_element_name,
               xercesc::ErrorHandler& eh);

        X
        parse (C const* uri,
               std::basic_string<C> const& root_element_name,
               xercesc::ErrorHandler& eh);


        // Parse URI or a local file using a user-provided SAX2XMLReader
        // object. Note that you must initialize the Xerces-C++ runtime
        // before calling these functions.
        //
        X
        parse (std::basic_string<C> const& uri,
               std::basic_string<C> const& root_element_name,
               xercesc::SAX2XMLReader& sax);

        X
        parse (C const* uri,
               std::basic_string<C> const& root_element_name,
               xercesc::SAX2XMLReader& sax);

      public:

        // System id is a "system" identifier of the resources (e.g.,
        // URI or a full file path). Public id is "public" identifier
        // of the resource (e.g., application-specific name or relative
        // file path). System id is used in diagnostics messages 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& is,
               std::basic_string<C> const& root_element_name);


        // Parse std::istream with user-provided ErrorHandler object. Note
        // that you must initialize the Xerces-C++ runtime before calling
        // this function.
        //
        X
        parse (std::istream& is,
               std::basic_string<C> const& root_element_name,
               xercesc::ErrorHandler& eh);


        // Parse std::istream using user-provided SAX2XMLReader object.
        // Note that you must initialize the Xerces-C++ runtime before
        // calling this function.
        //
        X
        parse (std::istream& is,
               std::basic_string<C> const& root_element_name,
               xercesc::SAX2XMLReader& sax);


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


        // Parse std::istream with a system id and a user-provided
        // ErrorHandler object. Note that you must initialize the Xerces-C++
        // runtime before calling this function.
        //
        X
        parse (std::istream& is,
               std::basic_string<C> const& system_id,
               std::basic_string<C> const& root_element_name,
               xercesc::ErrorHandler& eh);


        // Parse std::istream with a system id and a user-provided
        // SAX2XMLReader object. Note that you must initialize the Xerces-C++
        // runtime before calling this function.
        //
        X
        parse (std::istream& is,
               std::basic_string<C> const& system_id,
               std::basic_string<C> const& root_element_name,
               xercesc::SAX2XMLReader& sax);


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


        // Parse std::istream with system and public ids and a user-provided
        // ErrorHandler object. Note that you must initialize the Xerces-C++
        // runtime before calling this function.
        //
        X
        parse (std::istream& is,
               std::basic_string<C> const& system_id,
               std::basic_string<C> const& public_id,
               std::basic_string<C> const& root_element_name,
               xercesc::ErrorHandler& eh);


        // Parse std::istream with system and public ids and a user-provided
        // SAX2XMLReader object. Note that you must initialize the Xerces-C++
        // runtime before calling this function.
        //
        X
        parse (std::istream& is,
               std::basic_string<C> const& system_id,
               std::basic_string<C> const& public_id,
               std::basic_string<C> const& root_element_name,
               xercesc::SAX2XMLReader& sax);

      public:

        // Parse InputSource. Note that you must initialize the Xerces-C++
        // runtime before calling this function.
        //
        X
        parse (xercesc::InputSource const& is,
               std::basic_string<C> const& root_element_name);


        // Parse InputSource with a user-provided ErrorHandler object. Note
        // that you must initialize the Xerces-C++ runtime before calling
        // this function.
        //
        X
        parse (xercesc::InputSource const& is,
               std::basic_string<C> const& root_element_name,
               xercesc::ErrorHandler& eh);


        // Parse InputSource using a user-provided SAX2XMLReader object. Note
        // that you must initialize the Xerces-C++ runtime before calling
        // this function.
        //
        X
        parse (xercesc::InputSource const& is,
               std::basic_string<C> const& root_element_name,
               xercesc::SAX2XMLReader& sax);

      protected:
        std::auto_ptr<xercesc::SAX2XMLReader>
        create_sax ();
      };


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

      public:

        // Parse URI or a local file.
        //
        void
        parse (std::basic_string<C> const& uri,
               std::basic_string<C> const& root_element_name);

        void
        parse (C const* uri,
               std::basic_string<C> const& root_element_name);

        void
        parse (std::basic_string<C> const& uri,
               std::basic_string<C> const& root_element_name,
               xercesc::ErrorHandler& eh);

        void
        parse (C const* uri,
               std::basic_string<C> const& root_element_name,
               xercesc::ErrorHandler& eh);

        void
        parse (std::basic_string<C> const& uri,
               std::basic_string<C> const& root_element_name,
               xercesc::SAX2XMLReader& sax);

        void
        parse (C const* uri,
               std::basic_string<C> const& root_element_name,
               xercesc::SAX2XMLReader& sax);

      public:

        // Parse std::istream.
        //
        void
        parse (std::istream& is,
               std::basic_string<C> const& root_element_name);

        void
        parse (std::istream& is,
               std::basic_string<C> const& root_element_name,
               xercesc::ErrorHandler& eh);

        void
        parse (std::istream& is,
               std::basic_string<C> const& root_element_name,
               xercesc::SAX2XMLReader& sax);

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

        void
        parse (std::istream& is,
               std::basic_string<C> const& system_id,
               std::basic_string<C> const& root_element_name,
               xercesc::ErrorHandler& eh);

        void
        parse (std::istream& is,
               std::basic_string<C> const& system_id,
               std::basic_string<C> const& root_element_name,
               xercesc::SAX2XMLReader& sax);

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

        void
        parse (std::istream& is,
               std::basic_string<C> const& system_id,
               std::basic_string<C> const& public_id,
               std::basic_string<C> const& root_element_name,
               xercesc::ErrorHandler& eh);

        void
        parse (std::istream& is,
               std::basic_string<C> const& system_id,
               std::basic_string<C> const& public_id,
               std::basic_string<C> const& root_element_name,
               xercesc::SAX2XMLReader& sax);

      public:

        // Parse InputSource.
        //
        void
        parse (xercesc::InputSource const& is,
               std::basic_string<C> const& root_element_name);

        void
        parse (xercesc::InputSource const& is,
               std::basic_string<C> const& root_element_name,
               xercesc::ErrorHandler& eh);

        void
        parse (xercesc::InputSource const& is,
               std::basic_string<C> const& root_element_name,
               xercesc::SAX2XMLReader& sax);

      protected:
        std::auto_ptr<xercesc::SAX2XMLReader>
        create_sax ();
      };

      // Template for xsd:list
      //
      template <typename X, typename Y, typename C>
      struct list: traverser<X, C>
      {
        // Parser hooks. Override them in your implementation.
        //
        virtual void
        item (Y const&)
        {
        }

        // Parser construction API.
        //
        void
        item (traverser<Y, C>& item)
        {
          item_ = &item;
        }

        void
        operator() (traverser<Y, C>& item)
        {
          item_ = &item;
        }

        // Implementation.
        //

        virtual void
        characters (std::basic_string<C> const& s)
        {
          //@@ In case of huge lists, it would be more efficient
          //   not to use "total" consolidation. Instead consolidate
          //   here until the next space.
          //

          // Note that according to the spec the separator is exactly
          // one space (0x20). This makes our life much easier.
          //

          for (std::size_t i (0), j (s.find (C (' ')));;)
          {
            if (j != std::basic_string<C>::npos)
            {

              item_->pre ();
              item_->characters (std::basic_string<C> (s, i, j - i));
              item (item_->post ());

              i = j + 1;
              j = s.find (C (' '), i);
            }
            else
            {
              // Last element.
              //

              item_->pre ();
              item_->characters (std::basic_string<C> (s, i));
              item (item_->post ());

              break;
            }
          }
        }

        list ()
            : item_ (0)
        {
        }

      private:
        traverser<Y, C>* item_;
      };


      //
      //
      template <typename C>
      struct document: virtual traverser_impl<C>
      {
        typedef std::basic_string<C> string;

        document (traverser_impl<C>& parser, string const& name)
            : parser_ (parser), name_ (name)
        {
        }

        virtual void
        start_element (string const& ns, string const& n)
        {
          if (name_ == n)
          {
            if (start_element_ (ns, n, &parser_))
              parser_.pre ();
          }
          else if (cur_)
          {
            traverser_impl<C>::start_element (ns, n);
          }
          else
            throw unexpected_element<C> (n, name_);
        }

        virtual void
        end_element (string const& ns, string const& n)
        {
          if (name_ == n)
          {
            // Note: no call to post ().
            //
            end_element_ (ns, n);
          }
          else if (cur_)
          {
            traverser_impl<C>::end_element (ns, n);
          }
          else
            throw unexpected_element<C> (n, name_);
        }

      protected:
        using traverser_impl<C>::cur_;

      private:
        traverser_impl<C>& parser_;
        string name_;
      };


      //
      //
      template <typename C>
      struct sax_event_router: xercesc::DefaultHandler
      {
        typedef std::basic_string<C> string;

        sax_event_router (traverser_base<C>& t)
            : t_ (t)
        {
        }

        // I know, some of those consts are stupid. But that's what
        // Xerces folks put into their interfaces and VC-7.1 thinks
        // there are different signatures if one strips this fluff off.
        //

        virtual void
        startElement(XMLCh const* const uri,
                     XMLCh const* const lname,
                     XMLCh const* const qname,
                     xercesc::Attributes const& attributes);

        virtual void
        endElement(XMLCh const* const uri,
                   XMLCh const* const lname,
                   XMLCh const* const qname);

        virtual void
        characters (XMLCh const* const s, unsigned int const length);

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

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

#endif  // XSD_CXX_PARSER_ELEMENTS_HXX
