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

#include <cassert>

#include <xsde/cxx/parser/non-validating/parser.hxx>

namespace xsde
{
  namespace cxx
  {
    namespace parser
    {
      namespace non_validating
      {
        // empty_content
        //

        void empty_content::
        _start_any_element (const ro_string&,
                            const ro_string&)
        {
        }

        void empty_content::
        _end_any_element (const ro_string&,
                          const ro_string&)
        {
        }

        void empty_content::
        _any_attribute (const ro_string&,
                        const ro_string&,
                        const ro_string&)
        {
        }

        void empty_content::
        _any_characters (const ro_string&)
        {
        }

        //
        //
        bool empty_content::
        _start_element_impl (const ro_string&,
                             const ro_string&)
        {
          return false;
        }

        bool empty_content::
        _end_element_impl (const ro_string&,
                           const ro_string&)
        {
          return false;
        }

        bool empty_content::
        _attribute_impl (const ro_string&,
                         const ro_string&,
                         const ro_string&)
        {
          return false;
        }

        bool empty_content::
        _characters_impl (const ro_string&)
        {
          return false;
        }

        //
        //
        void empty_content::
        _start_element (const ro_string& ns,
                        const ro_string& name)
        {
          if (!_start_element_impl (ns, name))
            _start_any_element (ns, name);
        }

        void empty_content::
        _end_element (const ro_string& ns,
                      const ro_string& name)
        {
          if (!_end_element_impl (ns, name))
            _end_any_element (ns, name);
        }

        static const char
        xmlns_namespace_[] = "http://www.w3.org/2000/xmlns/";

        static const char
        xsi_namespace_[] = "http://www.w3.org/2001/XMLSchema-instance";

        static const char
        type_[] = "type";

        static const char
        nil_[] = "nil";

        static const char
        schema_location_[] = "schemaLocation";

        static const char
        no_namespace_schema_location_[] = "noNamespaceSchemaLocation";

        void empty_content::
        _attribute (const ro_string& ns,
                    const ro_string& name,
                    const ro_string& value)
        {
          // Weed out special attributes: xsi:type, xsi:nil,
          // xsi:schemaLocation and noNamespaceSchemaLocation.
          // See section 3.2.7 in Structures for details.
          //
          if (ns == xsi_namespace_ &&
              (name == schema_location_ ||
               name == no_namespace_schema_location_ ||
               name == type_ ||
               name == nil_))
            return;

          // Also some parsers supply us with namespace-prefix
          // mapping attributes.
          //
          if (ns == xmlns_namespace_)
            return;

          if (!_attribute_impl (ns, name, value))
            _any_attribute (ns, name, value);
        }

        void empty_content::
        _characters (const ro_string& s)
        {
          if (!_characters_impl (s))
            _any_characters (s);
        }


        // simple_content
        //

        void simple_content::
        _attribute (const ro_string& ns,
                    const ro_string& name,
                    const ro_string& value)
        {
          // Weed out special attributes: xsi:type, xsi:nil,
          // xsi:schemaLocation and noNamespaceSchemaLocation.
          // See section 3.2.7 in Structures for details.
          //
          if (ns == xsi_namespace_ &&
              (name == schema_location_ ||
               name == no_namespace_schema_location_ ||
               name == type_ ||
               name == nil_))
            return;

          // Also some parsers supply us with namespace-prefix
          // mapping attributes.
          //
          if (ns == xmlns_namespace_)
            return;

          if (!_attribute_impl (ns, name, value))
            _any_attribute (ns, name, value);
        }

        void simple_content::
        _characters (const ro_string& str)
        {
          _characters_impl (str);
        }


        // complex_content
        //


        void complex_content::
        _start_element (const ro_string& ns,
                        const ro_string& name)
        {
          parse_state& s = context_.top ();

          if (s.depth_++ > 0)
          {
            if (s.any_)
              _start_any_element (ns, name);
            else if (s.parser_)
            {
              s.parser_->_start_element (ns, name);

              // Propagate error.
              //
#ifndef XSDE_EXCEPTIONS
              if (s.parser_->_error_p ())
                _copy_error (s.parser_);
#endif
            }
          }
          else
          {
            if (!_start_element_impl (ns, name))
            {
              _start_any_element (ns, name);
              s.any_ = true;
            }
            else if (s.parser_ != 0)
            {
              s.parser_->_pre_impl ();

              // Propagate error.
              //
#ifndef XSDE_EXCEPTIONS
              if (s.parser_->_error_p ())
                _copy_error (s.parser_);
#endif
            }
          }
        }

        void complex_content::
        _end_element (const ro_string& ns,
                      const ro_string& name)
        {
          // To understand what's going on here it is helpful to think of
          // a "total depth" as being the sum of individual depths over
          // all elements.
          //

          if (context_.top ().depth_ == 0)
          {
            parse_state& s = context_.under_top (); // One before last.

            if (--s.depth_ > 0)
            {
              // Indirect recursion.
              //
              if (s.parser_)
              {
                s.parser_->_end_element (ns, name);

                // Propagate error.
                //
#ifndef XSDE_EXCEPTIONS
                if (s.parser_->_error_p ())
                  _copy_error (s.parser_);
#endif
              }
            }
            else
            {
              // Direct recursion.
              //
              assert (this == s.parser_);

              _post_impl ();

#ifndef XSDE_EXCEPTIONS
              if (!_error_p ())
#endif
                if (!_end_element_impl (ns, name))
                  assert (false);
            }
          }
          else
          {
            parse_state& s = context_.top ();

            if (--s.depth_ > 0)
            {
              if (s.any_)
                _end_any_element (ns, name);
              else if (s.parser_)
              {
                s.parser_->_end_element (ns, name);

                // Propagate error.
                //
#ifndef XSDE_EXCEPTIONS
                if (s.parser_->_error_p ())
                  _copy_error (s.parser_);
#endif
              }
            }
            else
            {
#ifndef XSDE_EXCEPTIONS
              bool er = false;
#endif
              if (s.parser_ != 0 && !s.any_)
              {
                s.parser_->_post_impl ();

                // Propagate error.
                //
#ifndef XSDE_EXCEPTIONS
                if (s.parser_->_error_p ())
                {
                  _copy_error (s.parser_);
                  er = true;
                }
#endif
              }

#ifndef XSDE_EXCEPTIONS
              if (!er)
#endif
                if (!_end_element_impl (ns, name))
                {
                  s.any_ = false;
                  _end_any_element (ns, name);
                }
            }
          }
        }

        void complex_content::
        _attribute (const ro_string& ns,
                    const ro_string& name,
                    const ro_string& value)
        {
          // Weed out special attributes: xsi:type, xsi:nil,
          // xsi:schemaLocation and noNamespaceSchemaLocation.
          // See section 3.2.7 in Structures for details.
          //
          if (ns == xsi_namespace_ &&
              (name == schema_location_ ||
               name == no_namespace_schema_location_ ||
               name == type_ ||
               name == nil_))
            return;

          // Also some parsers supply us with namespace-prefix
          // mapping attributes.
          //
          if (ns == xmlns_namespace_)
            return;

          const parse_state& s = context_.top ();

          if (s.depth_ > 0)
          {
            if (s.any_)
              _any_attribute (ns, name, value);
            else if (s.parser_)
            {
              s.parser_->_attribute (ns, name, value);

              // Propagate error.
              //
#ifndef XSDE_EXCEPTIONS
              if (s.parser_->_error_p ())
                _copy_error (s.parser_);
#endif
            }
          }
          else
          {
            if (!_attribute_impl (ns, name, value))
              _any_attribute (ns, name, value);
          }
        }

        void complex_content::
        _characters (const ro_string& str)
        {
          const parse_state& s = context_.top ();

          if (s.depth_ > 0)
          {
            if (s.any_)
              _any_characters (str);
            else if (s.parser_)
            {
              s.parser_->_characters (str);

              // Propagate error.
              //
#ifndef XSDE_EXCEPTIONS
              if (s.parser_->_error_p ())
                _copy_error (s.parser_);
#endif
            }
          }
          else
          {
            if (!_characters_impl (str))
              _any_characters (str);
          }
        }

        void complex_content::
        _pre_impl ()
        {
#ifdef XSDE_EXCEPTIONS
          context_.push ();
          _pre ();
#else
          if (context_.push ())
            _sys_error (sys_error::no_memory);

          if (!_error_p ())
            _pre ();
#endif
        }

        void complex_content::
        _post_impl ()
        {
          _post ();
          context_.pop ();
        }

        // list_base
        //

        // Find first non-space character.
        //
        static ro_string::size_type
        find_ns (const char* s,
                 ro_string::size_type size,
                 ro_string::size_type pos)
        {
          while (pos < size &&
                 (s[pos] == 0x20 || s[pos] == 0x0A ||
                  s[pos] == 0x0D || s[pos] == 0x09))
            ++pos;

          return pos < size ? pos : ro_string::npos;
        }

        // Find first space character.
        //
        static ro_string::size_type
        find_s (const char* s,
                ro_string::size_type size,
                ro_string::size_type pos)
        {
          while (pos < size &&
                 s[pos] != 0x20 && s[pos] != 0x0A &&
                 s[pos] != 0x0D && s[pos] != 0x09)
            ++pos;

          return pos < size ? pos : ro_string::npos;
        }

        // Relevant XML Schema Part 2: Datatypes sections: 4.2.1.2, 4.3.6.
        //

        void list_base::
        _pre ()
        {
#ifdef XSDE_EXCEPTIONS
          buf_.assign ("", 0);
#else
          if (buf_.assign ("", 0))
            _sys_error (sys_error::no_memory);
#endif
        }

        void list_base::
        _characters (const ro_string& s)
        {
          typedef ro_string::size_type size_type;

          const char* data = s.data ();
          size_type size = s.size ();

          // Handle the previous chunk if we start with a ws.
          //
          if (!buf_.empty () &&
              (data[0] == 0x20 || data[0] == 0x0A ||
               data[0] == 0x0D || data[0] == 0x09))
          {
            ro_string tmp (buf_); // Private copy ctor.
            _xsde_parse_item (tmp);
            buf_.assign ("", 0); // Can't fail.
          }

          // Traverse the data while logically collapsing spaces.
          //
          for (size_type i = find_ns (data, size, 0); i != ro_string::npos;)
          {
            size_type j = find_s (data, size, i);

            if (j != ro_string::npos)
            {
              if (buf_.empty ())
              {
                ro_string tmp (data + i, j - i); // Private copy ctor.
                _xsde_parse_item (tmp);
              }
              else
              {
                // Assemble the first item in str from buf_ and s.
                //
                string str;
                str.swap (buf_);

#ifdef XSDE_EXCEPTIONS
                str.append (data + i, j - i);
#else
                if (str.append (data + i, j - i))
                {
                  _sys_error (sys_error::no_memory);
                  break;
                }
#endif
                ro_string tmp (str); // Private copy ctor.
                _xsde_parse_item (tmp);
              }

#ifndef XSDE_EXCEPTIONS
              if (_error_p ())
                break;
#endif

              i = find_ns (data, size, j);
            }
            else
            {
              // Last fragment, append it to buf_.
              //
#ifdef XSDE_EXCEPTIONS
              buf_.append (data + i, size - i);
#else
              if (buf_.append (data + i, size - i))
                _sys_error (sys_error::no_memory);
#endif
              break;
            }
          }
        }

        void list_base::
        _post ()
        {
          // Handle the last item.
          //
          if (!buf_.empty ())
          {
            ro_string tmp (buf_); // Private copy ctor.
            _xsde_parse_item (tmp);
          }
        }
      }
    }
  }
}
