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

#include <cxx/tree/serialization-source.hxx>

#include <xsd-frontend/semantic-graph.hxx>
#include <xsd-frontend/traversal.hxx>

namespace CXX
{
  namespace Tree
  {
    namespace
    {
      enum schema_type
      {
        st_other,
        st_double,
        st_decimal
      };

      enum schema_type
      schema_type (SemanticGraph::Type& t)
      {
        if (t.is_a<SemanticGraph::Fundamental::Double> ())
          return st_double;
        else if (t.is_a<SemanticGraph::Fundamental::Decimal> ())
          return st_decimal;
        else
          return st_other;
      }


      //
      //
      struct List : Traversal::List, protected virtual Context
      {
        List (Context& c)
            : Context (c)
        {
        }

        virtual Void
        traverse (Type& l)
        {
          String name (ename (l));

          // If renamed name is empty then we do not need to generate
          // anything for this type.
          //
          if (renamed_type (l, name) && !name)
            return;

          SemanticGraph::Type& item_type (l.argumented ().type ());
          String base (L"::xsd::cxx::tree::list< " +
                       item_type_name (item_type) + L", " + char_type);

          if (item_type.is_a<SemanticGraph::Fundamental::Double> ())
            base += L", ::xsd::cxx::tree::schema_type::double_";
          else if (item_type.is_a<SemanticGraph::Fundamental::Decimal> ())
            base += L", ::xsd::cxx::tree::schema_type::decimal";

          base += L" >";

          // operator<< (xercesc::DOMElement)
          //
          os << "void" << endl
             << "operator<< (" << xerces_ns << "::DOMElement& e, " <<
            "const " << name << "& i)"
             << "{"
             << "e << static_cast< const " << base << "& > (i);"
             << "}";

          // operator<< (xercesc::DOMAttr)
          //
          os << "void" << endl
             << "operator<< (" << xerces_ns << "::DOMAttr& a, " <<
            "const " << name << "& i)"
             << "{"
             << "a << static_cast< const " << base << "& > (i);"
             << "}";

          // operator<< (list_stream)
          //
          os << "void" << endl
             << "operator<< (" << list_stream_type << "& l," << endl
             << "const " << name << "& i)"
             << "{"
             << "l << static_cast< const " << base << "& > (i);"
             << "}";

          // Register with type factory map.
          //
          if (polymorphic && !l.context ().count ("anonymous"))
          {
            // Note that we are using the original type name.
            //
            os << "static" << endl
               << "const ::xsd::cxx::tree::type_serializer_initializer< 0, " <<
              char_type << ", " << ename (l) << " >" << endl
               << "_xsd_" << ename (l) << "_type_serializer_init (" << endl
               << L << strlit (l.name ()) << "," << endl
               << L << strlit (xml_ns_name (l)) << ");"
               << endl
               << endl;
          }
        }

      private:
        String
        item_type_name (SemanticGraph::Type& t)
        {
          std::wostringstream o;

          MemberTypeName type (*this, o);
          type.dispatch (t);

          return o.str ();
        }
      };


      struct Union : Traversal::Union, protected virtual Context
      {
        Union (Context& c)
            : Context (c)
        {
        }

        virtual Void
        traverse (Type& u)
        {
          String name (ename (u));

          // If renamed name is empty then we do not need to generate
          // anything for this type.
          //
          if (renamed_type (u, name) && !name)
            return;

          String const& base (xs_string_type);

          // operator<< (xercesc::DOMElement)
          //
          os << "void" << endl
             << "operator<< (" << xerces_ns << "::DOMElement& e, " <<
            "const " << name << "& i)"
             << "{"
             << "e << static_cast< const " << base << "& > (i);"
             << "}";

          // operator<< (xercesc::DOMAttr)
          //
          os << "void" << endl
             << "operator<< (" << xerces_ns << "::DOMAttr& a, " <<
            "const " << name << "& i)"
             << "{"
             << "a << static_cast< const " << base << "& > (i);"
             << "}";

          // operator<< (list_stream)
          //
          os << "void" << endl
             << "operator<< (" << list_stream_type << "& l," << endl
             << "const " << name << "& i)"
             << "{"
             << "l << static_cast< const " << base << "& > (i);"
             << "}";

          // Register with type factory map.
          //
          if (polymorphic && !u.context ().count ("anonymous"))
          {
            // Note that we are using the original type name.
            //
            os << "static" << endl
               << "const ::xsd::cxx::tree::type_serializer_initializer< 0, " <<
              char_type << ", " << ename (u) << " >" << endl
               << "_xsd_" << ename (u) << "_type_serializer_init (" << endl
               << L << strlit (u.name ()) << "," << endl
               << L << strlit (xml_ns_name (u)) << ");"
               << endl
               << endl;
          }
        }
      };


      struct Enumeration : Traversal::Enumeration, protected virtual Context
      {
        Enumeration (Context& c)
            : Context (c), base_ (c)
        {
          inherits_base_ >> base_;
        }

        virtual Void
        traverse (Type& e)
        {
          String name (ename (e));

          // If renamed name is empty then we do not need to generate
          // anything for this type.
          //
          if (renamed_type (e, name) && !name)
            return;

          // operator<< (xercesc::DOMElement)
          //
          os << "void" << endl
             << "operator<< (" << xerces_ns << "::DOMElement& e, " <<
            "const " << name << "& i)"
             << "{"
             << "e << static_cast< const ";

          inherits (e, inherits_base_);

          os << "& > (i);"
             << "}";


          // operator<< (xercesc::DOMAttr)
          //
          os << "void" << endl
             << "operator<< (" << xerces_ns << "::DOMAttr& a, " <<
            "const " << name << "& i)"
             << "{"
             << "a << static_cast< const ";

          inherits (e, inherits_base_);

          os << "& > (i);"
             << "}";


          // operator<< (list_stream)
          //
          os << "void" << endl
             << "operator<< (" << list_stream_type << "& l," << endl
             << "const " << name << "& i)"
             << "{"
             << "l << static_cast< const ";

          inherits (e, inherits_base_);

          os << "& > (i);"
             << "}";


          // Register with type factory map.
          //
          if (polymorphic && !e.context ().count ("anonymous"))
          {
            // Note that we are using the original type name.
            //
            os << "static" << endl
               << "const ::xsd::cxx::tree::type_serializer_initializer< 0, " <<
              char_type << ", " << ename (e) << " >" << endl
               << "_xsd_" << ename (e) << "_type_serializer_init (" << endl
               << L << strlit (e.name ()) << "," << endl
               << L << strlit (xml_ns_name (e)) << ");"
               << endl
               << endl;
          }
        }

      private:
        Traversal::Inherits inherits_base_;
        BaseTypeName base_;
      };

      struct Element : Traversal::Element, protected virtual Context
      {
        Element (Context& c, String const& scope_)
            : Context (c), scope (scope_)
        {
        }

        virtual Void
        traverse (Type& e)
        {
          if (skip (e))
            return;

          String const& aname (eaname (e));
          String ns (e.qualified () ? e.namespace_ ().name () : "");
          String type (scope + L"::" + etype (e));

          // Check if we need to handle xsi:type and substitution groups.
          // If this element's type is anonymous or mapped to a fundamental
          // C++ type then we don't need to do anything. Note that if the
          // type is anonymous then it can't be derived from which makes it
          // impossible to substitute or dynamically-type with xsi:type.
          //
          SemanticGraph::Type& t (e.type ());
          Boolean poly (polymorphic && !t.context ().count ("anonymous"));

          if (poly)
          {
            Boolean fund (false);
            IsFundamentalType traverser (fund);
            traverser.dispatch (t);
            poly = !fund;
          }

          os << "// " << comment (e.name ()) << endl
             << "//" << endl;

          // aCC cannot handle an inline call to type_serializer_map_instance.
          //
          if (poly)
          {
            os << "{"
               << "::xsd::cxx::tree::type_serializer_map< " << char_type
               << " >& tsm (" << endl
               << "::xsd::cxx::tree::type_serializer_map_instance< 0, " <<
              char_type << " > ());"
               << endl;
          }

          if (max (e) != 1)
          {
            // sequence
            //
            os << "for (" << scope << "::" << econst_iterator (e) << endl
               << "b (i." << aname << " ().begin ()), " <<
              "n (i." << aname << " ().end ());" << endl
               << "b != n; ++b)"
               << "{";

            if (poly)
            {
              os << "if (typeid (" << type << ") == typeid (*b))"
                 << "{"
                 << xerces_ns << "::DOMElement& s (" << endl
                 << "::xsd::cxx::xml::dom::create_element (" << endl
                 << L << strlit (e.name ()) << "," << endl
                 << (ns ? L + strlit (ns) + L",\n" : L"")
                 << "e));"
                 << endl
                 << "s << *b;"
                 << "}"
                 << "else" << endl
                 << "tsm.serialize (" << endl
                 << L << strlit (e.name ()) << "," << endl
                 << L << strlit (ns) << "," << endl
                 << (e.global () ? "true" : "false") << ", " <<
                (e.qualified () ? "true" : "false") << ", e, *b);";
            }
            else
            {
              os << xerces_ns << "::DOMElement& s (" << endl
                 << "::xsd::cxx::xml::dom::create_element (" << endl
                 << L << strlit (e.name ()) << "," << endl
                 << (ns ? L + strlit (ns) + L",\n" : L"")
                 << "e));"
                 << endl;

              switch (schema_type (t))
              {
              case st_other:
                {
                  os << "s << *b;";
                  break;
                }
              case st_double:
                {
                  os << "s << " << as_double_type << " (*b);";
                  break;
                }
              case st_decimal:
                {
                  os << "s << " << as_decimal_type << " (*b);";
                  break;
                }
              }
            }

            os << "}";
          }
          else if (min (e) == 0)
          {
            // optional
            //
            os << "if (i." << aname << " ())"
               << "{";

            if (poly)
            {
              os << "const " << type << "& x (*i." << aname << " ());"
                 << "if (typeid (" << type << ") == typeid (x))"
                 << "{"
                 << xerces_ns << "::DOMElement& s (" << endl
                 << "::xsd::cxx::xml::dom::create_element (" << endl
                 << L << strlit (e.name ()) << "," << endl
                 << (ns ? L + strlit (ns) + L",\n" : L"")
                 << "e));"
                 << endl
                 << "s << x;"
                 << "}"
                 << "else" << endl
                 << "tsm.serialize (" << endl
                 << L << strlit (e.name ()) << "," << endl
                 << L << strlit (ns) << "," << endl
                 << (e.global () ? "true" : "false") << ", " <<
                (e.qualified () ? "true" : "false") << ", e, x);";
            }
            else
            {
              os << xerces_ns << "::DOMElement& s (" << endl
                 << "::xsd::cxx::xml::dom::create_element (" << endl
                 << L << strlit (e.name ()) << "," << endl
                 << (ns ? L + strlit (ns) + L",\n" : L"")
                 << "e));"
                 << endl;

              switch (schema_type (t))
              {
              case st_other:
                {
                  os << "s << *i." << aname << " ();";
                  break;
                }
              case st_double:
                {
                  os << "s << " << as_double_type << "(*i." << aname << " ());";
                  break;
                }
              case st_decimal:
                {
                  os << "s << " << as_decimal_type << "(*i." << aname << " ());";
                  break;
                }
              }
            }

            os << "}";
          }
          else
          {
            // one
            //
            if (poly)
            {
              os << "const " << type << "& x (i." << aname << " ());"
                 << "if (typeid (" << type << ") == typeid (x))"
                 << "{"
                 << xerces_ns << "::DOMElement& s (" << endl
                 << "::xsd::cxx::xml::dom::create_element (" << endl
                 << L << strlit (e.name ()) << "," << endl
                 << (ns ? L + strlit (ns) + L",\n" : L"")
                 << "e));"
                 << endl
                 << "s << x;"
                 << "}"
                 << "else" << endl
                 << "tsm.serialize (" << endl
                 << L << strlit (e.name ()) << "," << endl
                 << L << strlit (ns) << "," << endl
                 << (e.global () ? "true" : "false") << ", " <<
                (e.qualified () ? "true" : "false") << ", e, x);";
            }
            else
            {
              os << "{"
                 << xerces_ns << "::DOMElement& s (" << endl
                 << "::xsd::cxx::xml::dom::create_element (" << endl
                 << L << strlit (e.name ()) << "," << endl
                 << (ns ? L + strlit (ns) + L",\n" : L"")
                 << "e));"
                 << endl;

              switch (schema_type (t))
              {
              case st_other:
                {
                  os << "s << i." << aname << " ();";
                  break;
                }
              case st_double:
                {
                  os << "s << " << as_double_type << "(i." << aname << " ());";
                  break;
                }
              case st_decimal:
                {
                  os << "s << " << as_decimal_type << "(i." << aname << " ());";
                  break;
                }
              }

              os << "}";
            }
          }

          if (poly)
            os << "}";
        }

      private:
        String scope;
      };

      struct Any : Traversal::Any, protected virtual Context
      {
        Any (Context& c, String const& scope_)
            : Context (c), scope (scope_)
        {
        }

        virtual Void
        traverse (Type& a)
        {
          String const& aname (eaname (a));

          os << "// " << ename (a) << endl
             << "//" << endl;

          if (max (a) != 1)
          {
            // sequence
            //
            os << "for (" << scope << "::" << econst_iterator (a) << endl
               << "b (i." << aname << " ().begin ()), " <<
              "n (i." << aname << " ().end ());" << endl
               << "b != n; ++b)"
               << "{"
               << "e.appendChild (" << endl
               << "e.getOwnerDocument ()->importNode (" << endl
               << "const_cast< " << xerces_ns <<
              "::DOMElement* > (&(*b)), true));"
               << "}";
          }
          else if (min (a) == 0)
          {
            // optional
            //
            os << "if (i." << aname << " ())"
               << "{"
               << "e.appendChild (" << endl
               << "e.getOwnerDocument ()->importNode (" << endl
               << "const_cast< " << xerces_ns << "::DOMElement* > (&(*i." <<
              aname << " ())), true));"
               << "}";
          }
          else
          {
            // one
            //
            os << "e.appendChild (" << endl
               << "e.getOwnerDocument ()->importNode (" << endl
               << "const_cast< " << xerces_ns << "::DOMElement* > (&(i." <<
              aname << " ())), true));"
               << endl;
          }
        }

      private:
        String scope;
      };

      struct Attribute : Traversal::Attribute, protected virtual Context
      {
        Attribute (Context& c, String const& scope_)
            : Context (c), scope (scope_)
        {
        }

        virtual Void
        traverse (Type& a)
        {
          String const& aname (eaname (a));
          String ns (a.qualified () ? a.namespace_ ().name () : "");

          os << "// " << comment (a.name ()) << endl
             << "//" << endl;

          Boolean def (a.default_ () && !is_qname (a.type ()));

          if (a.optional () && !def)
          {
            os << "if (i." << aname << " ())"
               << "{"
               << xerces_ns << "::DOMAttr& a (" << endl
               << "::xsd::cxx::xml::dom::create_attribute (" << endl
               << L << strlit (a.name ()) << "," << endl
               << (ns ? L + strlit (ns) + L",\n" : L"")
               << "e));"
               << endl;

            switch (schema_type (a.type ()))
            {
            case st_other:
              {
                os << "a << *i." << aname << " ();";
                break;
              }
            case st_double:
              {
                os << "a << " << as_double_type << "(*i." << aname << " ());";
                break;
              }
            case st_decimal:
              {
                os << "a << " << as_decimal_type << "(*i." << aname << " ());";
                break;
              }
            }

            os << "}";
          }
          else
          {
            // Make sure we serialize required fixed attributes.
            //
            if (a.optional () &&
                options.value<CLI::omit_default_attributes> ())
            {
              os << "if (i." << aname << " () != " << scope <<
                "::" << edefault_value (a) << " ())";
            }

            os << "{"
               << xerces_ns << "::DOMAttr& a (" << endl
               << "::xsd::cxx::xml::dom::create_attribute (" << endl
               << L << strlit (a.name ()) << "," << endl
               << (ns ? L + strlit (ns) + L",\n" : L"")
               << "e));"
               << endl;

            switch (schema_type (a.type ()))
            {
            case st_other:
              {
                os << "a << i." << aname << " ();";
                break;
              }
            case st_double:
              {
                os << "a << " << as_double_type << "(i." << aname << " ());";
                break;
              }
            case st_decimal:
              {
                os << "a << " << as_decimal_type << "(i." << aname << " ());";
                break;
              }
            }

            os << "}";
          }
        }

      private:
        String scope;
      };

      struct AnyAttribute : Traversal::AnyAttribute,
                            protected virtual Context
      {
        AnyAttribute (Context& c, String const& scope_)
            : Context (c), scope (scope_)
        {
        }

        virtual Void
        traverse (Type& a)
        {
          String const& aname (eaname (a));

          os << "// " << ename (a) << endl
             << "//" << endl;

          os << "for (" << scope << "::" << econst_iterator (a) << endl
             << "b (i." << aname << " ().begin ()), " <<
            "n (i." << aname << " ().end ());" << endl
             << "b != n; ++b)"
             << "{"
             << xerces_ns << "::DOMAttr* a (" << endl
             << "static_cast< " << xerces_ns << "::DOMAttr* > (" << endl
             << "e.getOwnerDocument ()->importNode (" << endl
             << "const_cast< " << xerces_ns << "::DOMAttr* > (&(*b)), true)));"
             << endl
             << "if (a->getLocalName () == 0)" << endl
             << "e.setAttributeNode (a);"
             << "else" << endl
             << "e.setAttributeNodeNS (a);"
             << "}";
        }

      private:
        String scope;
      };

      struct Complex : Traversal::Complex, protected virtual Context
      {
        Complex (Context& c)
            : Context (c), base_ (c)
        {
          inherits_ >> base_;
        }

        virtual Void
        traverse (Type& c)
        {
          String name (ename (c));

          // If renamed name is empty then we do not need to generate
          // anything for this type.
          //
          if (renamed_type (c, name) && !name)
            return;

          // operator<< (xercesc::DOMElement)
          //
          os << "void" << endl
             << "operator<< (" << xerces_ns << "::DOMElement& e, " <<
            "const " << name << "& i)"
             << "{";

          if (c.inherits_p ())
          {
            os << "e << static_cast< const ";
            inherits (c, inherits_);
            os << "& > (i);"
               << endl;
          }
          else
            os << "e << static_cast< const " << any_type << "& > (i);"
               << endl;

          // Serialize anyAttribute content first so that is gets
          // overriden by schema-defined attributes.
          //
          if (options.value<CLI::generate_wildcard> ())
          {
            AnyAttribute any_attribute (*this, name);
            Traversal::Names names (any_attribute);

            Complex::names (c, names);
          }

          {
            Traversal::Names names;
            Any any (*this, name);
            Element element (*this, name);
            Attribute attribute (*this, name);

            names >> element;
            names >> attribute;

            if (options.value<CLI::generate_wildcard> ())
              names >> any;

            Complex::names (c, names);
          }

          os << "}";


          Boolean simple (true);
          {
            IsSimpleType t (simple);
            t.dispatch (c);
          }

          if (simple)
          {
            Boolean hb (c.inherits_p ());

            // operator<< (xercesc::DOMAttr)
            //
            os << "void" << endl
               << "operator<< (" << xerces_ns << "::DOMAttr&" <<
              (hb ? " a" : "") << "," <<
              "const " << name << "&" << (hb ? " i" : "") << ")"
               << "{";

            if (hb)
            {
              os << "a << static_cast< const ";

              inherits (c, inherits_);

              os << "& > (i);";
            }

            os << "}";


            // operator<< (list_stream)
            //
            os << "void" << endl
               << "operator<< (" << list_stream_type << "&" <<
              (hb ? " l" : "") << "," << endl
               << "const " << name << "&" << (hb ? " i" : "") << ")"
               << "{";

            if (hb)
            {
              os << "l << static_cast< const ";

              inherits (c, inherits_);

              os << "& > (i);";
            }

            os << "}";
          }

          // Register with type factory map.
          //
          if (polymorphic && !c.context ().count ("anonymous"))
          {
            // Note that we are using the original type name.
            //
            os << "static" << endl
               << "const ::xsd::cxx::tree::type_serializer_initializer< 0, " <<
              char_type << ", " << ename (c) << " >" << endl
               << "_xsd_" << ename (c) << "_type_serializer_init (" << endl
               << L << strlit (c.name ()) << "," << endl
               << L << strlit (xml_ns_name (c)) << ");"
               << endl
               << endl;
          }
        }

      private:
        Traversal::Inherits inherits_;
        BaseTypeName base_;
      };


      // Generate substitution group map entry.
      //
      struct GlobalMember : Traversal::Element,
                            GlobalElementBase,
                            protected virtual Context
      {
        GlobalMember (Context& c)
            : Context (c), GlobalElementBase (c), type_name_ (c)
        {
          belongs_ >> type_name_;
        }

        virtual Void
        traverse (Type& e)
        {
          if (!generate_p (e))
            return;

          if (e.substitutes_p () && polymorphic)
          {
            Type& r (e.substitutes ().root ());

            String const& name (ename (e));

            os << "static" << endl
               << "const ::xsd::cxx::tree::type_serializer_initializer< 0, " <<
              char_type << ", ";

            belongs (e, belongs_);

            os << " >" << endl
               << "_xsd_" << name << "_element_name_init (" << endl
               << L << strlit (r.name ()) << "," << endl
               << L << strlit (r.namespace_ ().name ()) << "," << endl
               << L << strlit (e.name ()) << "," << endl
               << L << strlit (e.namespace_ ().name ()) << ");"
               << endl
               << endl;
          }
        }

      private:
        Traversal::Belongs belongs_;
        MemberTypeName type_name_;

      };

      struct Serializer: Traversal::Element,
                         GlobalElementBase,
                         protected virtual Context
      {
        Serializer (Context& c)
            : Context (c), GlobalElementBase (c)
        {
        }

        virtual Void
        traverse (Type& e)
        {
          if (!doc_root_p (e))
            return;

          String const& name (eserializer (e));
          String ns (e.namespace_ ().name ());
          String const& error_handler (error_handler_type);
          String const& namespace_infomap (namespace_infomap_type);

          SemanticGraph::Type& type (e.type ());

          // Note that I am using fq-name in function calls because g++ gets
          // confused if the name is 'type'. (see tests/schema/anonymous)
          //

          Boolean fund (false);
          {
            IsFundamentalType t (fund);
            t.dispatch (type);
          }

          // Check if we need to handle xsi:type and substitution groups.
          // If this element's type is anonymous or mapped to a fundamental
          // C++ type then we don't need to do anything.
          //
          Boolean poly (!fund && polymorphic &&
                        !type.context ().count ("anonymous"));

          // To std::ostream.
          //
          os << "void" << endl
             << name << " (::std::ostream& o," << endl
             << "const " << type_name (e) << "& s," << endl
             << "const " << namespace_infomap << "& m," << endl
             << "const " << string_type << "& e," << endl
             << flags_type << " f)"
             << "{"
             << "::xsd::cxx::xml::auto_initializer i (" << endl
             << "(f & " << flags_type << "::dont_initialize) == 0);"
             << endl
             << dom_auto_ptr << "< " << xerces_ns <<
            "::DOMDocument > d (" << endl
             << fq_name (e, "serializer") << " (s, m, f));"
             << endl
             << "::xsd::cxx::tree::error_handler< " << char_type << " > h;"
             << endl
             << "::xsd::cxx::xml::dom::ostream_format_target t (o);"
             << "if (!::xsd::cxx::xml::dom::serialize (t, *d, e, h, f))"
             << "{"
             << "h.throw_if_failed< ::xsd::cxx::tree::serialization< " <<
            char_type << " > > ();"
             << "}"
             << "}";

          os << "void" << endl
             << name << " (::std::ostream& o," << endl
             << "const " << type_name (e) << "& s," << endl
             << error_handler << "& h," << endl
             << "const " << namespace_infomap << "& m," << endl
             << "const " << string_type << "& e," << endl
             << flags_type << " f)"
             << "{"
             << "::xsd::cxx::xml::auto_initializer i (" << endl
             << "(f & " << flags_type << "::dont_initialize) == 0);"
             << endl
             << dom_auto_ptr << "< " << xerces_ns <<
            "::DOMDocument > d (" << endl
             << fq_name (e, "serializer") << " (s, m, f));"
             << "::xsd::cxx::xml::dom::ostream_format_target t (o);"
             << "if (!::xsd::cxx::xml::dom::serialize (t, *d, e, h, f))"
             << "{"
             << "throw ::xsd::cxx::tree::serialization< " <<
            char_type << " > ();"
             << "}"
             << "}";

          os << "void" << endl
             << name << " (::std::ostream& o," << endl
             << "const " << type_name (e) << "& s," << endl
             << xerces_ns << "::DOMErrorHandler& h," << endl
             << "const " << namespace_infomap << "& m," << endl
             << "const " << string_type << "& e," << endl
             << flags_type << " f)"
             << "{"
             << dom_auto_ptr << "< " << xerces_ns <<
            "::DOMDocument > d (" << endl
             << fq_name (e, "serializer") << " (s, m, f));"
             << "::xsd::cxx::xml::dom::ostream_format_target t (o);"
             << "if (!::xsd::cxx::xml::dom::serialize (t, *d, e, h, f))"
             << "{"
             << "throw ::xsd::cxx::tree::serialization< " <<
            char_type << " > ();"
             << "}"
             << "}";

          // To XMLFormatTarget.
          //
          os << "void" << endl
             << name << " (" << xerces_ns << "::XMLFormatTarget& t," << endl
             << "const " << type_name (e) << "& s," << endl
             << "const " << namespace_infomap << "& m," << endl
             << "const " << string_type << "& e," << endl
             << flags_type << " f)"
             << "{"
             << dom_auto_ptr << "< " << xerces_ns <<
            "::DOMDocument > d (" << endl
             << fq_name (e, "serializer") << " (s, m, f));"
             << endl
             << "::xsd::cxx::tree::error_handler< " << char_type << " > h;"
             << endl
             << "if (!::xsd::cxx::xml::dom::serialize (t, *d, e, h, f))"
             << "{"
             << "h.throw_if_failed< ::xsd::cxx::tree::serialization< " <<
            char_type << " > > ();"
             << "}"
             << "}";

          os << "void" << endl
             << name << " (" << xerces_ns << "::XMLFormatTarget& t," << endl
             << "const " << type_name (e) << "& s," << endl
             << error_handler << "& h," << endl
             << "const " << namespace_infomap << "& m," << endl
             << "const " << string_type << "& e," << endl
             << flags_type << " f)"
             << "{"
             << dom_auto_ptr << "< " << xerces_ns <<
            "::DOMDocument > d (" << endl
             << fq_name (e, "serializer") << " (s, m, f));"
             << "if (!::xsd::cxx::xml::dom::serialize (t, *d, e, h, f))"
             << "{"
             << "throw ::xsd::cxx::tree::serialization< " <<
            char_type << " > ();"
             << "}"
             << "}";

          os << "void" << endl
             << name << " (" << xerces_ns << "::XMLFormatTarget& t," << endl
             << "const " << type_name (e) << "& s," << endl
             << xerces_ns << "::DOMErrorHandler& h," << endl
             << "const " << namespace_infomap << "& m," << endl
             << "const " << string_type << "& e," << endl
             << flags_type << " f)"
             << "{"
             << dom_auto_ptr << "< " << xerces_ns <<
            "::DOMDocument > d (" << endl
             << fq_name (e, "serializer") << " (s, m, f));"
             << "if (!::xsd::cxx::xml::dom::serialize (t, *d, e, h, f))"
             << "{"
             << "throw ::xsd::cxx::tree::serialization< " <<
            char_type << " > ();"
             << "}"
             << "}";

          // To an existing DOM instance.
          //
          os << "void" << endl
             << name << " (" << xerces_ns << "::DOMDocument& d," << endl
             << "const " << type_name (e) << "& s," << endl
             << flags_type << ")"
             << "{"
             << xerces_ns << "::DOMElement& e (*d.getDocumentElement ());"
             << "const " << qname_type << " n (" << endl
             << "::xsd::cxx::xml::dom::name< " << char_type << " > (e));"
             << endl;

          if (poly)
          {
            os << "if (typeid (" << type_name (e) << ") == typeid (s))"
               << "{";
          }

          os << "if (n.name () == " << L << strlit (e.name ()) << " &&" << endl
             << "n.namespace_ () == " << L << strlit (ns) << ")"
             << "{";

          switch (schema_type (type))
          {
          case st_other:
            {
              os << "e << s;";
              break;
            }
          case st_double:
            {
              os << "e << " << as_double_type << "(s);";
              break;
            }
          case st_decimal:
            {
              os << "e << " << as_decimal_type << "(s);";
              break;
            }
          }

          os << "}"
             << "else"
             << "{"
             << "throw ::xsd::cxx::tree::unexpected_element < " <<
            char_type << " > (" << endl
             << "n.name ()," << endl
             << "n.namespace_ ()," << endl
             << L << strlit (e.name ()) << "," << endl
             << L << strlit (ns) << ");"
             << "}";

          if (poly)
          {
            // aCC cannot handle an inline call to
            // type_serializer_map_instance.
            //
            os << "}"
               << "else"
               << "{"
               << "::xsd::cxx::tree::type_serializer_map< " << char_type
               << " >& tsm (" << endl
               << "::xsd::cxx::tree::type_serializer_map_instance< 0, " <<
              char_type << " > ());"
               << endl
               << "tsm.serialize (" << endl
               << L << strlit (e.name ()) << "," << endl
               << L << strlit (e.namespace_().name ()) << "," << endl
               << "e, n, s);"
               << "}";
          }

          os << "}";


          // To a new DOM instance.
          //
          os << dom_auto_ptr << "< " << xerces_ns << "::DOMDocument >" << endl
             << name << " (const " << type_name (e) << "& s," << endl
             << "const " << namespace_infomap << "& m," << endl
             << flags_type << " f)"
             << "{";

          if (poly)
          {
            // aCC cannot handle an inline call to
            // type_serializer_map_instance as well as the direct
            // auto_ptr assignment.
            //
            os << dom_auto_ptr << "< " << xerces_ns << "::DOMDocument > d;"
               << endl
               << "if (typeid (" << type_name (e) << ") == typeid (s))"
               << "{"
               << dom_auto_ptr << "< " << xerces_ns <<
              "::DOMDocument > r (" << endl
               << "::xsd::cxx::xml::dom::serialize< " <<
              char_type << " > (" << endl
               << L << strlit (e.name ()) << "," << endl
               << L << strlit (ns) << "," << endl
               << "m, f));"
               << "d = r;"
               << "}"
               << "else"
               << "{"
               << "::xsd::cxx::tree::type_serializer_map< " << char_type
               << " >& tsm (" << endl
               << "::xsd::cxx::tree::type_serializer_map_instance< 0, " <<
              char_type << " > ());"
               << endl
               << dom_auto_ptr << "< " << xerces_ns <<
              "::DOMDocument > r (" << endl
               << "tsm.serialize (" << endl
               << L << strlit (e.name ()) << "," << endl
               << L << strlit (e.namespace_().name ()) << "," << endl
               << "m, s, f));"
               << "d = r;"
               << "}";
          }
          else
          {
            os << dom_auto_ptr << "< " << xerces_ns <<
              "::DOMDocument > d (" << endl
               << "::xsd::cxx::xml::dom::serialize< " <<
              char_type << " > (" << endl
               << L << strlit (e.name ()) << "," << endl
               << L << strlit (ns) << "," << endl
               << "m, f));"
               << endl;
          }

          os << fq_name (e, "serializer") << " (*d, s, f);"
             << "return d;"
             << "}";

        }

      private:
        String
        type_name (Type& e)
        {
          std::wostringstream o;

          MemberTypeName type (*this, o);
          type.dispatch (e.type ());

          return o.str ();
        }
      };
    }

    Void
    generate_serialization_source (Context& ctx,
                                   UnsignedLong first,
                                   UnsignedLong last)
    {
      ctx.os << "#include <ostream>" << endl
             << "#include <xsd/cxx/xml/dom/serialization-source.hxx>" << endl
             << "#include <xsd/cxx/tree/error-handler.hxx>" << endl
             << endl;

      if (ctx.polymorphic)
      {
        ctx.os << "#include <xsd/cxx/tree/type-serializer-map.hxx>" << endl
               << endl;

        Boolean import_maps (ctx.options.value<CLI::import_maps> ());
        Boolean export_maps (ctx.options.value<CLI::export_maps> ());

        if (import_maps || export_maps)
        {
          ctx.os << "#ifdef _MSC_VER" << endl
                 << endl
                 << "namespace xsd"
                 << "{"
                 << "namespace cxx"
                 << "{"
                 << "namespace tree"
                 << "{";

          if (export_maps)
            ctx.os << "template struct __declspec (dllexport) " <<
              "type_serializer_plate< 0, " << ctx.char_type << " >;";

          if (import_maps)
            ctx.os << "template struct __declspec (dllimport) " <<
              "type_serializer_plate< 0, " << ctx.char_type << " >;";

          ctx.os << "}" // tree
                 << "}" // cxx
                 << "}" // xsd
                 << "#endif // _MSC_VER" << endl
                 << endl;
        }

        ctx.os << "namespace _xsd"
               << "{"
               << "static" << endl
               << "const ::xsd::cxx::tree::type_serializer_plate< 0, " <<
          ctx.char_type << " >" << endl
               << "type_serializer_plate_init;"
               << "}";
      }

      Traversal::Schema schema;

      Traversal::Sources sources;
      Traversal::Names names_ns, names;

      Namespace ns (ctx, first, last);

      List list (ctx);
      Union union_ (ctx);
      Complex complex (ctx);
      Enumeration enumeration (ctx);
      GlobalMember member (ctx);
      Serializer serializer (ctx);


      schema >> sources >> schema;
      schema >> names_ns >> ns >> names;

      names >> list;
      names >> union_;
      names >> complex;
      names >> enumeration;
      names >> member;
      names >> serializer;


      schema.dispatch (ctx.schema_root);
    }
  }
}
