// file      : xsd/cxx/parser/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_ELEMENTS_HXX
#define XSD_CXX_PARSER_ELEMENTS_HXX

#include <string>

#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 ()
        {
        }

        // These functions are called when "unexpected" content
        // is encountered. Use them to handle "type-less" content
        // such as mixed content models, any/anyAttribute, and
        // anyType/anySimpleType. By default these functions do
        // nothing.
        //
        virtual void
        _start_unexpected_element (std::basic_string<C> const&, // namespace
                                   std::basic_string<C> const&) // name
        {
        }

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

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

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

        //
        //
        //
        virtual void
        _start_element (std::basic_string<C> const& ns,
                        std::basic_string<C> const& name)
        {
          if (cur_)
            cur_->_start_element (ns, name);
          else
            _start_unexpected_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);
          else
            _end_unexpected_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);
          else
            _unexpected_attribute (ns, name, value);
        }

        virtual void
        _characters (std::basic_string<C> const& s)
        {
          if (cur_)
            cur_->_characters (s);
          else
            _unexpected_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 C>
      struct document: virtual traverser_impl<C>
      {
        typedef std::basic_string<C> string;

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

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

        virtual void
        _end_element (string const& ns, string const& n)
        {
          if (name_ == n && ns_ == ns)
          {
            // 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, ns, name_, ns_);
        }

      protected:
        using traverser_impl<C>::cur_;

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


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

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

        void
        operator() (T<I, 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:
        T<I, C>* item_;
      };
    }
  }
}

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

#endif  // XSD_CXX_PARSER_ELEMENTS_HXX
