// file      : xsde/cxx/parser/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/validating/parser.hxx>

namespace xsde
{
  namespace cxx
  {
    namespace parser
    {
      namespace 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))
            schema_error (schema_error::unexpected_element);

          // _unexpected_element (ns, name);
        }

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

          // _unexpected_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))
            schema_error (schema_error::unexpected_attribute);

          // _unexpected_attribute (ns, name, value);
        }

        void empty_content::
        _characters (const ro_string& s)
        {
          if (!_characters_impl (s))
            schema_error (schema_error::unexpected_characters);

          // _unexpected_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))
            schema_error (schema_error::unexpected_attribute);

          // _unexpected_attribute (ns, name, value);
        }

        void simple_content::
        _characters (const ro_string& str)
        {
          if (!_characters_impl (str))
          {
            // Mixed content is implemented in the generated code
            // by overriding _characters_impl and forwarding to
            // _any_characters.
            //

            // Scan the string for any non-whitespace characters
            // (Structures, section 3.4.4, clause 1.3).
            //
            // Scan the string for any non-whitespace characters
            // (Structures, section 3.4.4, clause 1.3).
            //
            ro_string::size_type end = str.size ();
            for (ro_string::size_type i = 0; i < end; ++i)
            {
              char c = str[i];

              if (c == 0x20 || c == 0x0A || c == 0x0D || c == 0x09)
                continue;

              schema_error (schema_error::unexpected_characters);
              break;
            }
          }
        }

        void simple_content::
        _pre_impl ()
        {
          _pre ();

          if (!error_p_ ())
            _pre_a_validate ();
        }

        void simple_content::
        _post_impl ()
        {
          _post_a_validate ();

          if (!error_p_ ())
            _post ();
        }

        void simple_content::
        _pre_a_validate ()
        {
        }

        void simple_content::
        _post_a_validate ()
        {
        }

        bool simple_content::
        _attribute_impl (const ro_string& ns,
                         const ro_string& name,
                         const ro_string& value)
        {
          // Returns true on error.
          //
          if (_attribute_impl_phase_one (ns, name, value))
            return true;

          return _attribute_impl_phase_two (ns, name, value);
        }

        bool simple_content::
        _attribute_impl_phase_one (const ro_string&,
                                   const ro_string&,
                                   const ro_string&)
        {
          return false;
        }

        bool simple_content::
        _attribute_impl_phase_two (const ro_string&,
                                   const ro_string&,
                                   const ro_string&)
        {
          return false;
        }


        // 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.
              //
              if (s.parser_->error_p_ ())
                copy_error_ (s.parser_);
            }
          }
          else
          {
            if (!_start_element_impl (ns, name))
            {
              schema_error (schema_error::unexpected_element);

              //_unexpected_element (ns, name);
            }
            else if (s.parser_ != 0)
            {
              s.parser_->_pre_impl ();

              // Propagate error.
              //
              if (s.parser_->error_p_ ())
                copy_error_ (s.parser_);
            }
          }
        }

        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.
                //
                if (s.parser_->error_p_ ())
                  copy_error_ (s.parser_);
              }
            }
            else
            {
              // Direct recursion.
              //
              assert (this == s.parser_);

              _post_impl ();


              if (!error_p_ ())
              {
                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.
                //
                if (s.parser_->error_p_ ())
                  copy_error_ (s.parser_);
              }
            }
            else
            {
              bool er = false;

              if (s.parser_ != 0 && !s.any_)
              {
                s.parser_->_post_impl ();

                // Propagate error.
                //
                if (s.parser_->error_p_ ())
                {
                  copy_error_ (s.parser_);
                  er = true;
                }
              }

              if (!er)
              {
                if (!_end_element_impl (ns, name))
                  schema_error (schema_error::unexpected_element);

                // _unexpected_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.
              //
              if (s.parser_->error_p_ ())
                copy_error_ (s.parser_);
            }
          }
          else
          {
            if (!_attribute_impl (ns, name, value))
              schema_error (schema_error::unexpected_attribute);

            // _unexpected_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.
              //
              if (s.parser_->error_p_ ())
                copy_error_ (s.parser_);
            }

          }
          else
          {
            if (!_characters_impl (str))
            {
              // Mixed content is implemented in the generated code
              // by overriding _characters_impl and forwarding to
              // _any_characters.
              //

              // Scan the string for any non-whitespace characters
              // (Structures, section 3.4.4, clause 1.3).
              //
	      ro_string::size_type end = str.size ();
              for (ro_string::size_type i = 0; i < end; ++i)
              {
                char c = str[i];

                if (c == 0x20 || c == 0x0A || c == 0x0D || c == 0x09)
                  continue;

                schema_error (schema_error::unexpected_characters);
                break;
                // _unexpected_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

          if (!error_p_ ())
            _pre_a_validate ();

          if (!error_p_ ())
            _pre_e_validate ();
        }

        void complex_content::
        _post_impl ()
        {
          _post_e_validate ();

          if (!error_p_ ())
            _post_a_validate ();

          if (!error_p_ ())
            _post ();

          context_.pop ();
        }

        void complex_content::
        _pre_e_validate ()
        {
        }

        void complex_content::
        _post_e_validate ()
        {
        }

        void complex_content::
        _pre_a_validate ()
        {
        }

        void complex_content::
        _post_a_validate ()
        {
        }

        bool complex_content::
        _attribute_impl (const ro_string& ns,
                         const ro_string& name,
                         const ro_string& value)
        {
          // Returns true on error.
          //
          if (_attribute_impl_phase_one (ns, name, value))
            return true;

          return _attribute_impl_phase_two (ns, name, value);
        }

        bool complex_content::
        _attribute_impl_phase_one (const ro_string&,
                                   const ro_string&,
                                   const ro_string&)
        {
          return false;
        }

        bool complex_content::
        _attribute_impl_phase_two (const ro_string&,
                                   const ro_string&,
                                   const ro_string&)
        {
          return false;
        }


        // 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);
              }

              if (error_p_ ())
                break;

              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);
          }
        }
      }
    }
  }
}
