// file      : xsd/cxx-cli/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_CLI_PARSER_ELEMENTS_HXX
#define XSD_CXX_CLI_PARSER_ELEMENTS_HXX

#using <System.dll>

#include <string> //@@ tmp

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

#include <xsd/cxx/xml/elements.hxx>

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

namespace xsd
{
  namespace cxx_cli
  {
    namespace parser
    {
      //
      //
      interface class ITraverser
      {
        void
        _start_element (System::String^,  // namespace
                        System::String^); // name

        void
        _end_element (System::String^,  // namespace
                      System::String^); // name

        void
        _attribute (System::String^,  // namespace
                    System::String^,  // name
                    System::String^); // value

        void
        _characters (System::String^)
      };


      //
      //
      public ref class TraverserBase: ITraverser
      {
      public:
        TraverserBase ()
            : count_ (0), cur_ (nullptr)
        {
        }

        // 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 (System::String^, // namespace
                                   System::String^) // name
        {
        }

        virtual void
        _end_unexpected_element (System::String^, // namespace
                                 System::String^) // name
        {
        }

        virtual void
        _unexpected_attribute (System::String^, // namespace
                               System::String^, // name
                               System::String^) // value
        {
        }

        virtual void
        _unexpected_characters (System::String^)
        {
        }

        //
        //
        //
        virtual void
        _start_element (System::String^ ns, System::String^ name)
        {
          if (cur_ != nullptr)
            cur_->_start_element (ns, name);
          else
            _start_unexpected_element (ns, name);
        }

        virtual void
        _end_element (System::String^ ns, System::String^ name)
        {
          if (cur_ != nullptr)
            cur_->_end_element (ns, name);
          else
            _end_unexpected_element (ns, name);
        }

        virtual void
        _attribute (System::String^ ns,
                    System::String^ name,
                    System::String^ value)
        {
          if (cur_ != nullptr)
            cur_->_attribute (ns, name, value);
          else
            _unexpected_attribute (ns, name, value);
        }

        virtual void
        _characters (System::String^ s)
        {
          if (cur_ != nullptr)
            cur_->_characters (s);
          else
            _unexpected_characters (s);
        }

      protected:
        virtual bool
        _start_element_ (System::String^ ns,
                         System::String^ n,
                         ITraverser^ cur);

        virtual bool
        _end_element_ (System::String^ ns, System::String^ n);

      protected:
        unsigned long count_;
        ITraverser^ cur_;
      };


      //
      //
      generic <typename X>
      public ref class Traverser: public TraverserBase
      {
      public:
        virtual X^
        post () = 0;


      public:

        //@@ Shouldn't these be const?
        //

        X^
        _parse (System::String const^ uri,
                System::String const^ root_element_name,
                System::String const^ root_element_namespace);
      };


      /*
      // 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_;
      };
      */


      //
      //
      public ref class document: public TraverserBase
      {
      public:
        document (TraverserBase^ parser,
                  System::String const^ name
                  System::String const^ ns)
            : parser_ (parser), name_ (name), ns_ (ns)
        {
        }

        virtual void
        _start_element (System::String^ ns, System::String^ n)
        {
          if (name_ == n && ns_ == ns)
          {
            if (_start_element_ (ns, n, parser_))
              parser_.pre ();
          }
          else if (cur_ != nullptr)
          {
            TraverserBase::_start_element (ns, n);
          }
          else
            throw unexpected_element<C> (n, name_);
        }

        virtual void
        _end_element (System::String^ ns, System::String^ n)
        {
          if (name_ == n && ns_ == ns)
          {
            // Note: no call to post ().
            //
            _end_element_ (ns, n);
          }
          else if (cur_)
          {
            TraverserBase::_end_element (ns, n);
          }
          else
            throw unexpected_element<C> (n, name_);
        }

      private:
        TraverserBase^ parser_;
        System::String name_;
        System::String ns_;
      };


      //
      //
      struct sax_event_router: xercesc::DefaultHandler
      {
        typedef std::basic_string<wchar_t> string;

        sax_event_router (TraverserBase& 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:
        TraverserBase& t_;
        string buf_;
      };
    }
  }
}

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

#endif  // XSD_CXX_CLI_PARSER_ELEMENTS_HXX
