// file      : xsd/cxx/parser/generator.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 <type-map/lexer.hxx>
#include <type-map/parser.hxx>
#include <type-map/type-map.hxx>

#include <cxx/parser/elements.hxx>
#include <cxx/parser/generator.hxx>

#include <cxx/parser/validator.hxx>
#include <cxx/parser/name-processor.hxx>
#include <cxx/parser/state-processor.hxx>
#include <cxx/parser/type-processor.hxx>

#include <cxx/parser/parser-header.hxx>
#include <cxx/parser/parser-inline.hxx>
#include <cxx/parser/parser-source.hxx>
#include <cxx/parser/parser-forward.hxx>

#include <cxx/parser/impl-header.hxx>
#include <cxx/parser/impl-source.hxx>
#include <cxx/parser/driver-source.hxx>

#include <cxx/parser/element-validation-source.hxx>
#include <cxx/parser/attribute-validation-source.hxx>
#include <cxx/parser/characters-validation-source.hxx>

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

#include <backend-elements/regex.hxx>
#include <backend-elements/indentation/cxx.hxx>
#include <backend-elements/indentation/sloc.hxx>
#include <backend-elements/indentation/clip.hxx>

#include <cult/containers/set.hxx>
#include <cult/containers/vector.hxx>

#include <boost/filesystem/fstream.hpp>

#include <iostream>

#include <usage.hxx>

#include "../../../libxsd/xsd/cxx/version.hxx"

using std::endl;
using std::wcerr;

using namespace XSDFrontend::SemanticGraph;

//
//
typedef
boost::filesystem::wifstream
WideInputFileStream;

typedef
boost::filesystem::wofstream
WideOutputFileStream;

typedef
boost::filesystem::ifstream
NarrowInputFileStream;

namespace CXX
{
  namespace
  {
    Char const copyright_gpl[] =
    "// Copyright (C) 2005-2008 Code Synthesis Tools CC\n"
    "//\n"
    "// This program was generated by CodeSynthesis XSD, an XML Schema to\n"
    "// C++ data binding compiler.\n"
    "//\n"
    "// This program is free software; you can redistribute it and/or modify\n"
    "// it under the terms of the GNU General Public License version 2 as\n"
    "// published by the Free Software Foundation.\n"
    "//\n"
    "// This program is distributed in the hope that it will be useful,\n"
    "// but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    "// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
    "// GNU General Public License for more details.\n"
    "//\n"
    "// You should have received a copy of the GNU General Public License\n"
    "// along with this program; if not, write to the Free Software\n"
    "// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n"
    "//\n"
    "// In addition, as a special exception, Code Synthesis Tools CC gives\n"
    "// permission to link this program with the Xerces-C++ library (or with\n"
    "// modified versions of Xerces-C++ that use the same license as Xerces-C++),\n"
    "// and distribute linked combinations including the two. You must obey\n"
    "// the GNU General Public License version 2 in all respects for all of\n"
    "// the code used other than Xerces-C++. If you modify this copy of the\n"
    "// program, you may extend this exception to your version of the program,\n"
    "// but you are not obligated to do so. If you do not wish to do so, delete\n"
    "// this exception statement from your version.\n"
    "//\n"
    "// Furthermore, Code Synthesis Tools CC makes a special exception for\n"
    "// the Free/Libre and Open Source Software (FLOSS) which is described\n"
    "// in the accompanying FLOSSE file.\n"
    "//\n\n";

    Char const copyright_proprietary[] =
    "// Copyright (C) 2005-2008 Code Synthesis Tools CC\n"
    "//\n"
    "// This program was generated by CodeSynthesis XSD, an XML Schema\n"
    "// to C++ data binding compiler, in the Proprietary License mode.\n"
    "// You should have received a proprietary license from Code Synthesis\n"
    "// Tools CC prior to generating this code. See the license text for\n"
    "// conditions.\n"
    "//\n\n";

    Char const copyright_impl[] =
    "// Not copyrighted - public domain.\n"
    "//\n"
    "// This sample parser implementation was generated by CodeSynthesis XSD,\n"
    "// an XML Schema to C++ data binding compiler. You may use it in your\n"
    "// programs without any restrictions.\n"
    "//\n\n";
  }

  namespace Parser
  {
    namespace CLI
    {
      extern Key char_type;
      extern Key type_map                 = "type-map";
      extern Key char_type                = "char-type";
      extern Key output_dir               = "output-dir";
      extern Key xml_parser               = "xml-parser";
      extern Key generate_inline          = "generate-inline";
      extern Key generate_validation      = "generate-validation";
      extern Key suppress_validation      = "suppress-validation";
      extern Key generate_polymorphic     = "generate-polymorphic";
      extern Key generate_noop_impl       = "generate-noop-impl";
      extern Key generate_print_impl      = "generate-print-impl";
      extern Key generate_test_driver     = "generate-test-driver";
      extern Key force_overwrite          = "force-overwrite";
      extern Key root_element_first       = "root-element-first";
      extern Key root_element_last        = "root-element-last";
      extern Key root_element             = "root-element";
      extern Key generate_xml_schema      = "generate-xml-schema";
      extern Key extern_xml_schema        = "extern-xml-schema";
      extern Key skel_type_suffix         = "skel-type-suffix";
      extern Key skel_file_suffix         = "skel-file-suffix";
      extern Key impl_type_suffix         = "impl-type-suffix";
      extern Key impl_file_suffix         = "impl-file-suffix";
      extern Key namespace_map            = "namespace-map";
      extern Key namespace_regex          = "namespace-regex";
      extern Key namespace_regex_trace    = "namespace-regex-trace";
      extern Key reserved_name            = "reserved-name";
      extern Key include_with_brackets    = "include-with-brackets";
      extern Key include_prefix           = "include-prefix";
      extern Key include_regex            = "include-regex";
      extern Key include_regex_trace      = "include-regex-trace";
      extern Key guard_prefix             = "guard-prefix";
      extern Key hxx_suffix               = "hxx-suffix";
      extern Key ixx_suffix               = "ixx-suffix";
      extern Key cxx_suffix               = "cxx-suffix";
      extern Key hxx_regex                = "hxx-regex";
      extern Key ixx_regex                = "ixx-regex";
      extern Key cxx_regex                = "cxx-regex";
      extern Key hxx_prologue             = "hxx-prologue";
      extern Key ixx_prologue             = "ixx-prologue";
      extern Key cxx_prologue             = "cxx-prologue";
      extern Key prologue                 = "prologue";
      extern Key hxx_epilogue             = "hxx-epilogue";
      extern Key ixx_epilogue             = "ixx-epilogue";
      extern Key cxx_epilogue             = "cxx-epilogue";
      extern Key epilogue                 = "epilogue";
      extern Key hxx_prologue_file        = "hxx-prologue-file";
      extern Key ixx_prologue_file        = "ixx-prologue-file";
      extern Key cxx_prologue_file        = "cxx-prologue-file";
      extern Key prologue_file            = "prologue-file";
      extern Key hxx_epilogue_file        = "hxx-epilogue-file";
      extern Key ixx_epilogue_file        = "ixx-epilogue-file";
      extern Key cxx_epilogue_file        = "cxx-epilogue-file";
      extern Key epilogue_file            = "epilogue-file";
      extern Key export_symbol            = "export-symbol";
      extern Key export_maps              = "export-maps";
      extern Key import_maps              = "import-maps";
      extern Key show_anonymous           = "show-anonymous";
      extern Key show_sloc                = "show-sloc";
      extern Key proprietary_license      = "proprietary-license";
    }
  }

  Void Parser::Generator::
  usage ()
  {
    std::wostream& e (wcerr);
    ::CLI::Indent::Clip< ::CLI::OptionsUsage, WideChar> clip (e);

    e << "--type-map <mapfile>" << endl
      << " Read XML Schema to C++ type mapping information\n"
      << " from <mapfile>. Repeat this option to specify\n"
      << " several type maps. Type maps are considered in\n"
      << " order of appearance and the first match is used."
      << endl;

    e << "--char-type <type>" << endl
      << " Use <type> as the base character type. Valid\n"
      << " values are 'char' (default) and 'wchar_t'."
      << endl;

    e << "--output-dir <dir>" << endl
      << " Write generated files to <dir> instead of current\n"
      << " directory."
      << endl;

    e << "--xml-parser <parser>" << endl
      << " Use <parser> as the underlying XML parser. Valid\n"
      << " values are 'xerces' (default) and 'expat'."
      << endl;

    e << "--generate-inline" << endl
      << " Generate certain functions inline."
      << endl;

    e << "--generate-validation" << endl
      << " Generate validation code."
      << endl;

    e << "--suppress-validation" << endl
      << " Suppress generating of validation code."
      << endl;

    e << "--generate-polymorphic" << endl
      << " Generate polymorphism-aware code. Specify this\n"
      << " option if you use substitution groups or xsi:type."
      << endl;

    e << "--generate-noop-impl" << endl
      << " Generate a sample parser implementation that\n"
      << " does nothing (no operation)."
      << endl;

    e << "--generate-print-impl" << endl
      << " Generate a sample parser implementation that\n"
      << " prints the XML data to STDOUT."
      << endl;

    e << "--generate-test-driver" << endl
      << " Generate a test driver for the sample parser\n"
      << " implementation."
      << endl;

    e << "--force-overwrite" << endl
      << " Force overwriting of the existing implementation\n"
      << " and test driver files."
      << endl;

    e << "--root-element-first" << endl
      << " Indicate that the first global element is the\n"
      << " document root."
      << endl;

    e << "--root-element-last" << endl
      << " Indicate that the last global element is the\n"
      << " document root."
      << endl;

    e << "--root-element <element>" << endl
      << " Indicate that <element> is the document root."
      << endl;

    e << "--generate-xml-schema" << endl
      << " Generate a C++ header file as if the schema being\n"
      << " compiled defines the XML Schema namespace."
      << endl;

    e << "--extern-xml-schema <file>" << endl
      << " Generate code as if the XML Schema namespace was\n"
      << " defined in <file> and xsd:included in the schema\n"
      << " being compiled."
      << endl;

    e << "--skel-type-suffix <suffix>" << endl
      << " Use <suffix> instead of the default '_pskel' to\n"
      << " construct the names of generated parser skeletons."
      << endl;

    e << "--skel-file-suffix <suffix>" << endl
      << " Use <suffix> instead of the default '-pskel' to\n"
      << " construct the names of generated parser skeleton\n"
      << " files."
      << endl;

    e << "--impl-type-suffix <suffix>" << endl
      << " Use <suffix> instead of the default '_pimpl' to\n"
      << " construct the names of parser implementations for\n"
      << " the built-in XML Schema types and sample parser\n"
      << " implementations."
      << endl;

    e << "--impl-file-suffix <suffix>" << endl
      << " Use <suffix> instead of the default '-pimpl' to\n"
      << " construct the names of generated sample parser\n"
      << " implementation files."
      << endl;

    e << "--namespace-map <xns>=<cns>" << endl
      << " Map XML Schema namespace <xns> to C++ namespace\n"
      << " <cns>. Repeat this option to specify mapping for\n"
      << " more than one XML Schema namespace."
      << endl;

    e << "--namespace-regex <regex>" << endl
      << " Add <regex> to the list of regular expressions\n"
      << " used to translate XML Schema namespace names to\n"
      << " C++ namespace names."
      << endl;

    e << "--namespace-regex-trace" << endl
      << " Trace the process of applying regular expressions\n"
      << " specified with the --namespace-regex option."
      << endl;

    e << "--reserved-name <name>" << endl
      << " Add <name> to the list of names that should not\n"
      << " be used as identifiers. The name can optionally\n"
      << " be followed by '=' and the replacement name that\n"
      << " should be used instead."
      << endl;

    e << "--include-with-brackets" << endl
      << " Use angle brackets (<>) instead of quotes (\"\") in\n"
      << " generated #include directives."
      << endl;

    e << "--include-prefix <prefix>" << endl
      << " Add <prefix> to generated #include directive\n"
      << " paths."
      << endl;

    e << "--include-regex <regex>" << endl
      << " Add <regex> to the list of regular expressions\n"
      << " used to transform #include directive paths."
      << endl;

    e << "--include-regex-trace" << endl
      << " Trace the process of applying regular expressions\n"
      << " specified with the --include-regex option."
      << endl;

    e << "--guard-prefix <prefix>" << endl
      << " Add <prefix> to generated header inclusion guards."
      << endl;

    e << "--hxx-suffix <suffix>" << endl
      << " Use <suffix> instead of the default '.hxx' to\n"
      << " construct the name of the header file."
      << endl;

    e << "--ixx-suffix <suffix>" << endl
      << " Use <suffix> instead of the default '.ixx' to\n"
      << " construct the name of the inline file."
      << endl;

    e << "--cxx-suffix <suffix>" << endl
      << " Use <suffix> instead of the default '.cxx' to\n"
      << " construct the name of the source file."
      << endl;

    e << "--hxx-regex <regex>" << endl
      << " Use <regex> to construct the name of the header\n"
      << " file."
      << endl;

    e << "--ixx-regex <regex>" << endl
      << " Use <regex> to construct the name of the inline\n"
      << " file."
      << endl;

    e << "--cxx-regex <regex>" << endl
      << " Use <regex> to construct the name of the source\n"
      << " file."
      << endl;


    // Prologues.
    //
    e << "--hxx-prologue <text>" << endl
      << " Insert <text> at the beginning of the header file."
      << endl;

    e << "--ixx-prologue <text>" << endl
      << " Insert <text> at the beginning of the inline file."
      << endl;

    e << "--cxx-prologue <text>" << endl
      << " Insert <text> at the beginning of the source file."
      << endl;

    e << "--prologue <text>" << endl
      << " Insert <text> at the beginning of each generated\n"
      << " file for which there is no file-specific prologue."
      << endl;


    // Epilogues.
    //
    e << "--hxx-epilogue <text>" << endl
      << " Insert <text> at the end of the header file."
      << endl;

    e << "--ixx-epilogue <text>" << endl
      << " Insert <text> at the end of the inline file."
      << endl;

    e << "--cxx-epilogue <text>" << endl
      << " Insert <text> at the end of the source file."
      << endl;

    e << "--epilogue <text>" << endl
      << " Insert <text> at the end of each generated file\n"
      << " for which there is no file-specific epilogue."
      << endl;


    // Prologue files.
    //
    e << "--hxx-prologue-file <file>" << endl
      << " Insert the content of the <file> at the beginning\n"
      << " of the header file."
      << endl;

    e << "--ixx-prologue-file <file>" << endl
      << " Insert the content of the <file> at the beginning\n"
      << " of the inline file."
      << endl;

    e << "--cxx-prologue-file <file>" << endl
      << " Insert the content of the <file> at the beginning\n"
      << " of the source file."
      << endl;

    e << "--prologue-file <file>" << endl
      << " Insert the content of the <file> at the beginning\n"
      << " of each generated file for which there is no file-\n"
      << " specific prologue file."
      << endl;


    // Epilogue files.
    //
    e << "--hxx-epilogue-file <file>" << endl
      << " Insert the content of the <file> at the end of\n"
      << " the header file."
      << endl;

    e << "--ixx-epilogue-file <file>" << endl
      << " Insert the content of the <file> at the end of\n"
      << " the inline file."
      << endl;

    e << "--cxx-epilogue-file <file>" << endl
      << " Insert the content of the <file> at the end of\n"
      << " the source file."
      << endl;

    e << "--epilogue-file <file>" << endl
      << " Insert the content of the <file> at the end of\n"
      << " each generated file for which there is no file-\n"
      << " specific epilogue file."
      << endl;


    // Misc.
    //
    e << "--export-symbol <symbol>" << endl
      << " Export symbol for Win32 DLL export/import control."
      << endl;

    e << "--export-maps" << endl
      << " Export polymorphism support maps from Win32 DLL."
      << endl;

    e << "--import-maps" << endl
      << " Import polymorphism support maps from Win32 DLL."
      << endl;

    e << "--show-anonymous" << endl
      << " Show elements and attributes that are of anonymous\n"
      << " types."
      << endl;

    e << "--show-sloc" << endl
      << " Show the number of generated physical source lines\n"
      << " of code (SLOC)."
      << endl;

    e << "--sloc-limit <num>" << endl
      << " Check that the number of generated physical source\n"
      << " lines of code (SLOC) does not exceed <num>."
      << endl;

    e << "--options-file <file>" << endl
      << " Read additional options from <file>. Each option\n"
      << " should appear on a separate line optionally\n"
      << " followed by space and an argument."
      << endl;

    e << "--proprietary-license" << endl
      << " Indicate that the generated code is licensed under\n"
      << " a proprietary license instead of the GPL."
      << endl;
  }

  Parser::CLI::OptionsSpec Parser::Generator::
  options_spec ()
  {
    CLI::OptionsSpec spec;

    spec.option<CLI::char_type> ().default_value ("char");
    spec.option<CLI::xml_parser> ().default_value ("xerces");

    spec.option<CLI::skel_file_suffix> ().default_value ("-pskel");
    spec.option<CLI::skel_type_suffix> ().default_value ("_pskel");
    spec.option<CLI::impl_file_suffix> ().default_value ("-pimpl");
    spec.option<CLI::impl_type_suffix> ().default_value ("_pimpl");

    spec.option<CLI::hxx_suffix> ().default_value (".hxx");
    spec.option<CLI::ixx_suffix> ().default_value (".ixx");
    spec.option<CLI::cxx_suffix> ().default_value (".cxx");

    return spec;
  }


  namespace
  {
    template <typename S>
    Void
    open (S& ifs, NarrowString const& path)
    {
      try
      {
        Path fs_path (path, boost::filesystem::native);
        ifs.open (fs_path, std::ios_base::in | std::ios_base::binary);

        if (!ifs.is_open ())
        {
          wcerr << path.c_str () << ": error: unable to open in read mode"
                << endl;

          throw Parser::Generator::Failed ();
        }
      }
      catch (InvalidPath const&)
      {
        wcerr << "error: '" << path.c_str () << "' is not a valid "
              << "filesystem path" << endl;

        throw Parser::Generator::Failed ();
      }
    }

    Void
    append (WideOutputFileStream& os,
            NarrowString const& path,
            WideInputFileStream& default_is)
    {
      using std::ios_base;

      if (path)
      {
        WideInputFileStream is;
        open (is, path);
        os << is.rdbuf ();
      }
      else if (default_is.is_open ())
      {
        os << default_is.rdbuf ();
        default_is.seekg (0, ios_base::beg);
      }
    }

    Void
    append (WideOutputFileStream& os,
            Cult::Containers::Vector<NarrowString> const& primary,
            Cult::Containers::Vector<NarrowString> const& def)
    {
      Cult::Containers::Vector<NarrowString> const& v (
        primary.empty () ? def : primary);

      for (Containers::Vector<NarrowString>::ConstIterator
             i (v.begin ()), e (v.end ()); i != e; ++i)
      {
        os << i->c_str () << endl;
      }
    }
  }


  UnsignedLong Parser::Generator::
  generate (Parser::CLI::Options const& ops,
            Schema& schema,
            Path const& file_path,
            Boolean gen_driver,
            const WarningSet& disabled_warnings,
            FileList& file_list,
            AutoUnlinks& unlinks)
  {
    using std::ios_base;
    namespace Indentation = BackendElements::Indentation;

    typedef BackendElements::Regex::Expression<Char> Regex;

    try
    {
      Boolean generate_xml_schema (ops.value<CLI::generate_xml_schema> ());

      // We could be compiling several schemas at once in which case
      // handling of the --generate-xml-schema option gets tricky: we
      // will need to rely on the presence of the --extern-xml-schema
      // to tell us which (fake) schema file corresponds to XML Schema.
      //
      if (generate_xml_schema)
      {
        if (NarrowString name = ops.value<CLI::extern_xml_schema> ())
        {
          if (file_path.native_file_string () != name)
            generate_xml_schema = false;
        }
      }

      Boolean impl (!generate_xml_schema &&
                    (ops.value<CLI::generate_noop_impl> () ||
                     ops.value<CLI::generate_print_impl> ()));

      Boolean driver (gen_driver && !generate_xml_schema &&
                      ops.value<CLI::generate_test_driver> ());

      // Evaluate the graph for possibility of generating something useful.
      //
      {
        Validator validator;
        if (!validator.validate (
              ops, schema, file_path, driver, disabled_warnings))
          throw Failed ();
      }

      // Process names.
      //
      {
        NameProcessor proc;
        proc.process (ops, schema, file_path);
      }

      Boolean validation ((ops.value<CLI::xml_parser> () == "expat" ||
                           ops.value<CLI::generate_validation> ()) &&
                          !ops.value<CLI::suppress_validation> ());

      // Compute state machine info.
      //
      if (validation)
      {
        StateProcessor proc;
        proc.process (schema, file_path);
      }

      // Read-in type maps.
      //
      TypeMap::Namespaces type_map;
      {
        using namespace TypeMap;
        typedef Containers::Vector<NarrowString> Files;

        Files const& files (ops.value<CLI::type_map> ());

        for (Files::ConstIterator f (files.begin ()); f != files.end (); ++f )
        {
          NarrowInputFileStream ifs;
          open (ifs, *f);

          Lexer l (ifs, *f);
          TypeMap::Parser p (l, *f);

          if (!p.parse (type_map))
            throw Failed ();
        }

        // Add the built-in mappings at the end.
        //

        // String-based types.
        //
        String char_type (ops.value<CLI::char_type> ());
        String string_type;

        if (char_type == L"char")
          string_type = L"::std::string";
        else if (char_type == L"wchar_t")
          string_type = L"::std::wstring";
        else
          string_type = L"::std::basic_string< " + char_type + L" >";

        String xns;
        {
          Context ctx (std::wcerr, schema, ops, 0, 0, 0);
          xns = ctx.xs_ns_name ();
        }

        String buffer (L"::std::auto_ptr< " + xns + L"::buffer >");
        TypeMap::Namespace xsd ("http://www\\.w3\\.org/2001/XMLSchema");

        xsd.types_push_back ("string", string_type);
        xsd.types_push_back ("normalizedString", string_type);
        xsd.types_push_back ("token", string_type);
        xsd.types_push_back ("Name", string_type);
        xsd.types_push_back ("NMTOKEN", string_type);
        xsd.types_push_back ("NMTOKENS", xns + L"::string_sequence");
        xsd.types_push_back ("NCName", string_type);

        xsd.types_push_back ("ID", string_type);
        xsd.types_push_back ("IDREF", string_type);
        xsd.types_push_back ("IDREFS", xns + L"::string_sequence");

        xsd.types_push_back ("language", string_type);
        xsd.types_push_back ("anyURI", string_type);
        xsd.types_push_back ("QName", xns + L"::qname");

        xsd.types_push_back ("base64Binary", buffer, buffer);
        xsd.types_push_back ("hexBinary", buffer, buffer);

        xsd.types_push_back ("gDay", xns + L"::gday");
        xsd.types_push_back ("gMonth", xns + L"::gmonth");
        xsd.types_push_back ("gYear", xns + L"::gyear");
        xsd.types_push_back ("gMonthDay", xns + L"::gmonth_day");
        xsd.types_push_back ("gYearMonth", xns + L"::gyear_month");
        xsd.types_push_back ("date", xns + L"::date");
        xsd.types_push_back ("time", xns + L"::time");
        xsd.types_push_back ("dateTime", xns + L"::date_time");
        xsd.types_push_back ("duration", xns + L"::duration");

        // Fundamental C++ types.
        //
        xsd.types_push_back ("boolean", "bool", "bool");

        xsd.types_push_back ("byte", "signed char", "signed char");
        xsd.types_push_back ("unsignedByte",
                             "unsigned char",
                             "unsigned char");

        xsd.types_push_back ("short", "short", "short");
        xsd.types_push_back ("unsignedShort",
                             "unsigned short",
                             "unsigned short");

        xsd.types_push_back ("int", "int", "int");
        xsd.types_push_back ("unsignedInt", "unsigned int", "unsigned int");

        xsd.types_push_back ("long", "long long", "long long");
        xsd.types_push_back ("unsignedLong",
                             "unsigned long long",
                             "unsigned long long");

        xsd.types_push_back ("integer", "long long", "long long");

        xsd.types_push_back ("negativeInteger", "long long", "long long");
        xsd.types_push_back ("nonPositiveInteger", "long long", "long long");

        xsd.types_push_back ("positiveInteger",
                             "unsigned long long",
                             "unsigned long long");
        xsd.types_push_back ("nonNegativeInteger",
                             "unsigned long long",
                             "unsigned long long");

        xsd.types_push_back ("float", "float", "float");
        xsd.types_push_back ("double", "double", "double");
        xsd.types_push_back ("decimal", "double", "double");

        type_map.push_back (xsd);

        // Everyhting else maps to void.
        //
        TypeMap::Namespace rest (".*");
        rest.types_push_back (".*", "void", "void");
        type_map.push_back (rest);
      }

      // Process types.
      //
      {
        TypeProcessor proc;
        proc.process (ops, schema, gen_driver, type_map);
      }

      //
      //
      Boolean inline_ (ops.value<CLI::generate_inline> () &&
                       !generate_xml_schema);

      Boolean source (!generate_xml_schema);

      // Generate code.
      //
      NarrowString name (file_path.leaf ());
      NarrowString skel_suffix (ops.value <CLI::skel_file_suffix> ());
      NarrowString impl_suffix (ops.value <CLI::impl_file_suffix> ());

      NarrowString hxx_suffix (ops.value <CLI::hxx_suffix> ());
      NarrowString ixx_suffix (ops.value <CLI::ixx_suffix> ());
      NarrowString cxx_suffix (ops.value <CLI::cxx_suffix> ());

      Regex hxx_expr (
        ops.value <CLI::hxx_regex> ().empty ()
        ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + hxx_suffix + "#"
        : ops.value <CLI::hxx_regex> ());

      Regex ixx_expr (
        ops.value <CLI::ixx_regex> ().empty ()
        ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + ixx_suffix + "#"
        : ops.value <CLI::ixx_regex> ());

      Regex cxx_expr (
        ops.value <CLI::cxx_regex> ().empty ()
        ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + cxx_suffix + "#"
        : ops.value <CLI::cxx_regex> ());


      Regex hxx_impl_expr;
      Regex cxx_impl_expr;
      Regex cxx_driver_expr;

      if (impl || driver)
      {
        hxx_impl_expr =
          "#^(.+?)(\\.[^./\\\\]+)?$#$1" + impl_suffix + hxx_suffix + "#";

        cxx_impl_expr =
          "#^(.+?)(\\.[^./\\\\]+)?$#$1" + impl_suffix + cxx_suffix + "#";

        cxx_driver_expr =
          "#^(.+?)(\\.[^./\\\\]+)?$#$1-driver" + cxx_suffix + "#";
      }

      if (!hxx_expr.match (name))
      {
        wcerr << "error: header expression '" <<
          hxx_expr.pattern () << "' does not match '" <<
          name.c_str () << "'" << endl;
        throw Failed ();
      }

      if (inline_ && !ixx_expr.match (name))
      {
        wcerr << "error: inline expression '" <<
          ixx_expr.pattern () << "' does not match '" <<
          name.c_str () << "'" << endl;
        throw Failed ();
      }

      if (source && !cxx_expr.match (name))
      {
        wcerr << "error: source expression '" <<
          cxx_expr.pattern () << "' does not match '" <<
          name.c_str () << "'" << endl;
        throw Failed ();
      }

      if (impl || driver)
      {
        if (!hxx_impl_expr.match (name))
        {
          wcerr << "error: implementation header expression '" <<
            hxx_impl_expr.pattern () << "' does not match '" <<
            name.c_str () << "'" << endl;
          throw Failed ();
        }

        if (!cxx_impl_expr.match (name))
        {
          wcerr << "error: implementation source expression '" <<
            cxx_impl_expr.pattern () << "' does not match '" <<
            name.c_str () << "'" << endl;
          throw Failed ();
        }

        if (!cxx_driver_expr.match (name))
        {
          wcerr << "error: driver source expression '" <<
            cxx_driver_expr.pattern () << "' does not match '" <<
            name.c_str () << "'" << endl;
          throw Failed ();
        }
      }

      NarrowString hxx_name (hxx_expr.merge (name));
      NarrowString ixx_name (inline_ ? ixx_expr.merge (name) : NarrowString ());
      NarrowString cxx_name (source ? cxx_expr.merge (name) : NarrowString ());

      NarrowString hxx_impl_name;
      NarrowString cxx_impl_name;
      NarrowString cxx_driver_name;

      if (impl || driver)
      {
        hxx_impl_name = hxx_impl_expr.merge (name);
        cxx_impl_name = cxx_impl_expr.merge (name);
        cxx_driver_name = cxx_driver_expr.merge (name);
      }

      Path hxx_path (hxx_name, boost::filesystem::native);
      Path ixx_path (ixx_name, boost::filesystem::native);
      Path cxx_path (cxx_name, boost::filesystem::native);

      Path hxx_impl_path;
      Path cxx_impl_path;
      Path cxx_driver_path;

      if (impl || driver)
      {
        hxx_impl_path = Path (hxx_impl_name, boost::filesystem::native);
        cxx_impl_path = Path (cxx_impl_name, boost::filesystem::native);
        cxx_driver_path = Path (cxx_driver_name, boost::filesystem::native);
      }

      if (NarrowString dir  = ops.value<CLI::output_dir> ())
      {
        try
        {
          Path path (dir, boost::filesystem::native);

          hxx_path = path / hxx_path;
          ixx_path = path / ixx_path;
          cxx_path = path / cxx_path;

          if (impl || driver)
          {
            hxx_impl_path = path / hxx_impl_path;
            cxx_impl_path = path / cxx_impl_path;
            cxx_driver_path = path /cxx_driver_path;
          }
        }
        catch (InvalidPath const&)
        {
          wcerr << dir.c_str () << ": error: invalid path" << endl;
          throw Failed ();
        }
      }

      // Open the impl files first so that if open fails, the skel files
      // are not deleted.
      //
      WideOutputFileStream hxx_impl;
      WideOutputFileStream cxx_impl;
      WideOutputFileStream cxx_driver;

      if (impl)
      {
        if (!ops.value<CLI::force_overwrite> ())
        {
          WideInputFileStream tmp (hxx_impl_path, ios_base::in);

          if (tmp.is_open ())
          {
            wcerr << hxx_impl_path << ": error: cowardly refusing to " <<
              "overwrite an existing file" << endl;
            throw Failed ();
          }

          tmp.close ();
        }

        hxx_impl.open (hxx_impl_path, ios_base::out);

        if (!hxx_impl.is_open ())
        {
          wcerr << hxx_impl_path << ": error: unable to open in write mode"
                << endl;
          throw Failed ();
        }

        unlinks.add (hxx_impl_path);
        file_list.push_back (hxx_impl_path.native_file_string ());

        if (!ops.value<CLI::force_overwrite> ())
        {
          WideInputFileStream tmp (cxx_impl_path, ios_base::in);

          if (tmp.is_open ())
          {
            wcerr << cxx_impl_path << ": error: cowardly refusing to " <<
              "overwrite an existing file" << endl;
            throw Failed ();
          }

          tmp.close ();
        }

        cxx_impl.open (cxx_impl_path, ios_base::out);

        if (!cxx_impl.is_open ())
        {
          wcerr << cxx_impl_path << ": error: unable to open in write mode"
                << endl;
          throw Failed ();
        }

        unlinks.add (cxx_impl_path);
        file_list.push_back (cxx_impl_path.native_file_string ());
      }

      if (driver)
      {
        if (!ops.value<CLI::force_overwrite> ())
        {
          WideInputFileStream tmp (cxx_driver_path, ios_base::in);

          if (tmp.is_open ())
          {
            wcerr << cxx_driver_path << ": error: cowardly refusing to " <<
              "overwrite an existing file" << endl;
            throw Failed ();
          }

          tmp.close ();
        }

        cxx_driver.open (cxx_driver_path, ios_base::out);

        if (!cxx_driver.is_open ())
        {
          wcerr << cxx_driver_path << ": error: unable to open in write " <<
            "mode" << endl;
          throw Failed ();
        }

        unlinks.add (cxx_driver_path);
        file_list.push_back (cxx_driver_path.native_file_string ());
      }

      // Open the skel files.
      //
      WideOutputFileStream hxx (hxx_path, ios_base::out);
      WideOutputFileStream ixx;
      WideOutputFileStream cxx;

      if (!hxx.is_open ())
      {
        wcerr << hxx_path << ": error: unable to open in write mode" << endl;
        throw Failed ();
      }

      unlinks.add (hxx_path);
      file_list.push_back (hxx_path.native_file_string ());

      if (inline_)
      {
        ixx.open (ixx_path, ios_base::out);

        if (!ixx.is_open ())
        {
          wcerr << ixx_path << ": error: unable to open in write mode" << endl;
          throw Failed ();
        }

        unlinks.add (ixx_path);
        file_list.push_back (ixx_path.native_file_string ());
      }


      if (source)
      {
        cxx.open (cxx_path, ios_base::out);

        if (!cxx.is_open ())
        {
          wcerr << cxx_path << ": error: unable to open in write mode" << endl;
          throw Failed ();
        }

        unlinks.add (cxx_path);
        file_list.push_back (cxx_path.native_file_string ());
      }

      // Print copyright and license.
      //
      Char const* copyright (
        ops.value<CLI::proprietary_license> ()
        ? copyright_proprietary
        : copyright_gpl);

      hxx << copyright;

      if (inline_)
        ixx << copyright;

      if (source)
        cxx << copyright;

      if (impl)
      {
        hxx_impl << copyright_impl;
        cxx_impl << copyright_impl;
      }

      if (driver)
        cxx_driver << copyright_impl;

      // Prologue.
      //
      WideInputFileStream prologue;
      {
        NarrowString name (ops.value<CLI::prologue_file> ());

        if (name)
          open (prologue, name);
      }

      // Epilogue.
      //
      WideInputFileStream epilogue;
      {
        NarrowString name (ops.value<CLI::epilogue_file> ());

        if (name)
          open (epilogue, name);
      }

      // SLOC counter.
      //
      UnsignedLong sloc (0);
      Boolean show_sloc (ops.value<CLI::show_sloc> ());

      //
      //
      Regex guard_expr ("/([a-z])([A-Z])/$1_$2/"); // Split words.

      NarrowString guard_prefix (ops.value<CLI::guard_prefix> ());

      if (!guard_prefix)
        guard_prefix = file_path.branch_path ().native_directory_string ();

      if (guard_prefix)
        guard_prefix += '_';

      // HXX
      //
      {
        Context ctx (hxx, schema, ops, &hxx_expr, &ixx_expr, &hxx_impl_expr);

        Indentation::Clip<Indentation::SLOC, WideChar> hxx_sloc (hxx);

        String guard (guard_expr.merge (guard_prefix + hxx_name));
        guard = ctx.escape (guard); // Make it a C++ id.
        std::transform (guard.begin (), guard.end(), guard.begin (), upcase);

        hxx << "#ifndef " << guard << endl
            << "#define " << guard << endl
            << endl;

        // Copy prologue.
        //
        hxx << "// Begin prologue." << endl
            << "//" << endl;

        append (
          hxx, ops.value<CLI::hxx_prologue> (), ops.value<CLI::prologue> ());
        append (hxx, ops.value<CLI::hxx_prologue_file> (), prologue);

        hxx << "//" << endl
            << "// End prologue." << endl
            << endl;

        // Version check.
        //
        hxx << "#include <xsd/cxx/config.hxx>" << endl
            << endl
            << "#if (XSD_INT_VERSION != " << XSD_INT_VERSION << "L)" << endl
            << "#error XSD runtime version mismatch" << endl
            << "#endif" << endl
            << endl;

        {
          hxx << "#include <xsd/cxx/pre.hxx>" << endl
              << endl;

          // Set auto-indentation.
          //
          Indentation::Clip<Indentation::CXX, WideChar> hxx_clip (hxx);


          // Generate.
          //
          if (!generate_xml_schema)
            generate_parser_forward (ctx);

          generate_parser_header (ctx, generate_xml_schema);


          if (inline_)
            hxx << "#include " << ctx.process_include_path (ixx_name) << endl;

          hxx << "#include <xsd/cxx/post.hxx>" << endl
              << endl;
        }

        // Copy epilogue.
        //
        hxx << "// Begin epilogue." << endl
            << "//" << endl;

        append (hxx, ops.value<CLI::hxx_epilogue_file> (), epilogue);
        append (
          hxx, ops.value<CLI::hxx_epilogue> (), ops.value<CLI::epilogue> ());

        hxx << "//" << endl
            << "// End epilogue." << endl
            << endl;

        hxx << "#endif // " << guard << endl;

        if (show_sloc)
        {
          wcerr << hxx_path << ": "
                << hxx_sloc.buffer ().count () << endl;

          sloc += hxx_sloc.buffer ().count ();
        }
      }


      // IXX
      //
      if (inline_)
      {
        Context ctx (ixx, schema, ops, &hxx_expr, &ixx_expr, &hxx_impl_expr);

        Indentation::Clip<Indentation::SLOC, WideChar> ixx_sloc (ixx);


        // Copy prologue.
        //
        ixx << "// Begin prologue." << endl
            << "//" << endl;

        append (
          ixx, ops.value<CLI::ixx_prologue> (), ops.value<CLI::prologue> ());
        append (ixx, ops.value<CLI::ixx_prologue_file> (), prologue);

        ixx << "//" << endl
            << "// End prologue." << endl
            << endl;

        {
          // Set auto-indentation.
          //
          Indentation::Clip<Indentation::CXX, WideChar> ixx_clip (ixx);


          // Generate.
          //
          generate_parser_inline (ctx);
        }

        // Copy epilogue.
        //
        ixx << "// Begin epilogue." << endl
            << "//" << endl;

        append (ixx, ops.value<CLI::ixx_epilogue_file> (), epilogue);
        append (
          ixx, ops.value<CLI::ixx_epilogue> (), ops.value<CLI::epilogue> ());

        ixx << "//" << endl
            << "// End epilogue." << endl
            << endl;

        if (show_sloc)
        {
          wcerr << ixx_path << ": "
                << ixx_sloc.buffer ().count () << endl;

          sloc += ixx_sloc.buffer ().count ();
        }
      }


      // CXX
      //
      if (source)
      {
        Context ctx (cxx, schema, ops, &hxx_expr, &ixx_expr, &hxx_impl_expr);

        Indentation::Clip<Indentation::SLOC, WideChar> cxx_sloc (cxx);

        // Copy prologue.
        //
        cxx << "// Begin prologue." << endl
            << "//" << endl;

        append (
          cxx, ops.value<CLI::cxx_prologue> (), ops.value<CLI::prologue> ());
        append (cxx, ops.value<CLI::cxx_prologue_file> (), prologue);

        cxx << "//" << endl
            << "// End prologue." << endl
            << endl;

        {
          // Set auto-indentation.
          //
          Indentation::Clip<Indentation::CXX, WideChar> cxx_clip (cxx);

          cxx << "#include " << ctx.process_include_path (hxx_name) << endl
              << endl;

          if (!inline_)
            generate_parser_inline (ctx);

          generate_parser_source (ctx);

          if (validation)
          {
            generate_element_validation_source (ctx);
            generate_attribute_validation_source (ctx);
            generate_characters_validation_source (ctx);
          }
        }

        // Copy epilogue.
        //
        cxx << "// Begin epilogue." << endl
            << "//" << endl;

        append (cxx, ops.value<CLI::cxx_epilogue_file> (), epilogue);
        append (
          cxx, ops.value<CLI::cxx_epilogue> (), ops.value<CLI::epilogue> ());

        cxx << "//" << endl
            << "// End epilogue." << endl
            << endl;

        if (show_sloc)
        {
          wcerr << cxx_path << ": "
                << cxx_sloc.buffer ().count () << endl;

          sloc += cxx_sloc.buffer ().count ();
        }
      }

      // HXX impl
      //
      if (impl)
      {
        Context ctx (hxx_impl, schema, ops,
                     &hxx_expr, &ixx_expr, &hxx_impl_expr);

        String guard (guard_expr.merge (guard_prefix + hxx_impl_name));
        guard = ctx.escape (guard); // Make it a C++ id.
        std::transform (guard.begin (), guard.end(), guard.begin (), upcase);

        hxx_impl << "#ifndef " << guard << endl
                 << "#define " << guard << endl
                 << endl;

        {
          // Set auto-indentation.
          //
          Indentation::Clip<Indentation::CXX, WideChar> clip (hxx_impl);

          hxx_impl << "#include " << ctx.process_include_path (hxx_name)
                   << endl << endl;

          generate_impl_header (ctx);
        }

        hxx_impl << "#endif // " << guard << endl;
      }

      // CXX impl
      //
      if (impl)
      {
        Context ctx (cxx_impl, schema, ops,
                     &hxx_expr, &ixx_expr, &hxx_impl_expr);

        // Set auto-indentation.
        //
        Indentation::Clip<Indentation::CXX, WideChar> clip (cxx_impl);

        cxx_impl << "#include " << ctx.process_include_path (hxx_impl_name)
                 << endl << endl;

        generate_impl_source (ctx);
      }

      // CXX driver
      //
      if (driver)
      {
        Context ctx (cxx_driver, schema, ops,
                     &hxx_expr, &ixx_expr, &hxx_impl_expr);

        // Set auto-indentation.
        //
        Indentation::Clip<Indentation::CXX, WideChar> clip (cxx_driver);

        cxx_driver << "#include " << ctx.process_include_path (hxx_impl_name)
                   << endl << endl;

        generate_driver_source (ctx);
      }

      return sloc;
    }
    catch (NoNamespaceMapping const& e)
    {
      wcerr << e.file () << ":" << e.line () << ":" << e.column ()
            << ": error: unable to map XML Schema namespace '" << e.ns ()
            << "' to C++ namespace" << endl;

      wcerr << e.file () << ":" << e.line () << ":" << e.column ()
            << ": info: use the --namespace-map or --namespace-regex option "
            << "to provide custom mapping" << endl;

      throw Failed ();
    }
    catch (InvalidNamespaceMapping const& e)
    {
      wcerr << "error: invalid XML to C++ namespace mapping specified: "
            << "'" << e.mapping () << "': " << e.reason () << endl;

      throw Failed ();
    }
    catch (BackendElements::Regex::Format<Char> const& e)
    {
      wcerr << "error: invalid regex: '" <<
        e.expression ().c_str () << "': " <<
        e.description ().c_str () << endl;

      throw Failed ();
    }
    catch (BackendElements::Regex::Format<WideChar> const& e)
    {
      wcerr << "error: invalid regex: '" <<
        e.expression () << "': " << e.description () << endl;

      throw Failed ();
    }
  }
}
