// file      : xsd/cxx/tree/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 <cxx/tree/generator.hxx>

#include <cxx/tree/elements.hxx>

#include <cxx/tree/counter.hxx>
#include <cxx/tree/validator.hxx>
#include <cxx/tree/name-processor.hxx>

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

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

#include <cxx/tree/stream-header.hxx>
#include <cxx/tree/stream-source.hxx>

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

#include <cxx/tree/stream-insertion-header.hxx>
#include <cxx/tree/stream-insertion-source.hxx>
#include <cxx/tree/stream-extraction-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;

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

  namespace Tree
  {
    namespace CLI
    {
      extern Key char_type                = "char-type";
      extern Key output_dir               = "output-dir";
      extern Key generate_polymorphic     = "generate-polymorphic";
      extern Key generate_serialization   = "generate-serialization";
      extern Key generate_inline          = "generate-inline";
      extern Key generate_ostream         = "generate-ostream";
      extern Key generate_doxygen         = "generate-doxygen";
      extern Key generate_comparison      = "generate-comparison";
      extern Key generate_default_ctor    = "generate-default-ctor";
      extern Key generate_from_base_ctor  = "generate-from-base-ctor";
      extern Key generate_wildcard        = "generate-wildcard";
      extern Key generate_insertion       = "generate-insertion";
      extern Key generate_extraction      = "generate-extraction";
      extern Key generate_forward         = "generate-forward";
      extern Key generate_xml_schema      = "generate-xml-schema";
      extern Key extern_xml_schema        = "extern-xml-schema";
      extern Key suppress_parsing         = "suppress-parsing";
      extern Key generate_intellisense    = "generate-intellisense";
      extern Key omit_default_attributes  = "omit-default-attributes";
      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 type_naming              = "type-naming";
      extern Key function_naming          = "function-naming";
      extern Key type_regex               = "type-regex";
      extern Key accessor_regex           = "accessor-regex";
      extern Key one_accessor_regex       = "one-accessor-regex";
      extern Key opt_accessor_regex       = "opt-accessor-regex";
      extern Key seq_accessor_regex       = "seq-accessor-regex";
      extern Key modifier_regex           = "modifier-regex";
      extern Key one_modifier_regex       = "one-modifier-regex";
      extern Key opt_modifier_regex       = "opt-modifier-regex";
      extern Key seq_modifier_regex       = "seq-modifier-regex";
      extern Key parser_regex             = "parser-regex";
      extern Key serializer_regex         = "serializer-regex";
      extern Key enumerator_regex         = "enumerator-regex";
      extern Key name_regex_trace         = "name-regex-trace";
      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 root_element_first       = "root-element-first";
      extern Key root_element_last        = "root-element-last";
      extern Key root_element_all         = "root-element-all";
      extern Key root_element_none        = "root-element-none";
      extern Key root_element             = "root-element";
      extern Key custom_type              = "custom-type";
      extern Key custom_type_regex        = "custom-type-regex";
      extern Key hxx_suffix               = "hxx-suffix";
      extern Key ixx_suffix               = "ixx-suffix";
      extern Key cxx_suffix               = "cxx-suffix";
      extern Key fwd_suffix               = "fwd-suffix";
      extern Key hxx_regex                = "hxx-regex";
      extern Key ixx_regex                = "ixx-regex";
      extern Key cxx_regex                = "cxx-regex";
      extern Key fwd_regex                = "fwd-regex";
      extern Key hxx_prologue             = "hxx-prologue";
      extern Key ixx_prologue             = "ixx-prologue";
      extern Key cxx_prologue             = "cxx-prologue";
      extern Key fwd_prologue             = "fwd-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 fwd_epilogue             = "fwd-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 fwd_prologue_file        = "fwd-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 fwd_epilogue_file        = "fwd-epilogue-file";
      extern Key epilogue_file            = "epilogue-file";
      extern Key parts                    = "parts";
      extern Key parts_suffix             = "parts-suffix";
      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 Tree::Generator::
  usage ()
  {
    std::wostream& e (wcerr);
    ::CLI::Indent::Clip< ::CLI::OptionsUsage, WideChar> clip (e);

    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 << "--generate-polymorphic" << endl
      << " Generate polymorphism-aware code. Specify this\n"
      << " option if you use substitution groups or xsi:type."
      << endl;

    e << "--generate-serialization" << endl
      << " Generate serialization functions. They convert an\n"
      << " in-memory representation back to XML."
      << endl;

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

    e << "--generate-ostream" << endl
      << " Generate ostream insertion operators."
      << endl;

    e << "--generate-doxygen" << endl
      << " Generate documentation comments in the Doxygen\n"
      << " format."
      << endl;

    e << "--generate-comparison" << endl
      << " Generate comparison operators."
      << endl;

    e << "--generate-default-ctor" << endl
      << " Generate default constructors even for types that\n"
      << " have required members."
      << endl;

    e << "--generate-from-base-ctor" << endl
      << " Generate from-base constructors."
      << endl;

    e << "--generate-wildcard" << endl
      << " Generate accessors/modifiers as well as parsing\n"
      << " and serialization code for XML Schema wildcards."
      << endl;

    e << "--generate-insertion <os>" << endl
      << " Generate data representation stream insertion\n"
      << " operators for the <os> output stream type."
      << endl;

    e << "--generate-extraction <is>" << endl
      << " Generate data representation stream extraction\n"
      << " constructors for the <is> input stream type."
      << endl;

    e << "--generate-forward" << endl
      << " Generate forward declaration file."
      << 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 << "--suppress-parsing" << endl
      << " Suppress generation of parsing functions."
      << endl;

    e << "--generate-intellisense" << endl
      << " Generate workarounds for IntelliSense bugs in\n"
      << " Visual Studio 2005 (8.0)."
      << endl;

    e << "--omit-default-attributes" << endl
      << " Omit attributes with default and fixed values\n"
      << " from the serialized XML documents."
      << 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 << "--type-naming <style>" << endl
      << " Specify the type naming convention that should be\n"
      << " used in the generated code. Valid styles are 'knr'\n"
      << " (default), 'ucc', and 'java'."
      << endl;

    e << "--function-naming <style>" << endl
      << " Specify the function naming convention that should\n"
      << " be used in the generated code. Valid styles are\n"
      << " 'knr' (default), 'lcc', and 'java'."
      << endl;

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

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

    e << "--one-accessor-regex <expr>" << endl
      << " Add <expr> to the list of regular expressions\n"
      << " used to translate XML Schema names of elements\n"
      << " and attributes with cardinality one to C++\n"
      << " accessor function names."
      << endl;

    e << "--opt-accessor-regex <expr>" << endl
      << " Add <expr> to the list of regular expressions\n"
      << " used to translate XML Schema names of elements\n"
      << " and attributes with cardinality optional to C++\n"
      << " accessor function names."
      << endl;

    e << "--seq-accessor-regex <expr>" << endl
      << " Add <expr> to the list of regular expressions\n"
      << " used to translate XML Schema names of elements\n"
      << " and attributes with cardinality sequence to C++\n"
      << " accessor function names."
      << endl;

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

    e << "--one-modifier-regex <expr>" << endl
      << " Add <expr> to the list of regular expressions\n"
      << " used to translate XML Schema names of elements\n"
      << " and attributes with cardinality one to C++\n"
      << " modifier function names."
      << endl;

    e << "--opt-modifier-regex <expr>" << endl
      << " Add <expr> to the list of regular expressions\n"
      << " used to translate XML Schema names of elements\n"
      << " and attributes with cardinality optional to C++\n"
      << " modifier function names."
      << endl;

    e << "--seq-modifier-regex <expr>" << endl
      << " Add <expr> to the list of regular expressions\n"
      << " used to translate XML Schema names of elements\n"
      << " and attributes with cardinality sequence to C++\n"
      << " modifier function names."
      << endl;

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

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

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

    e << "--name-regex-trace" << endl
      << " Trace the process of applying regular expressions\n"
      << " specified with the name transformation options."
      << 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 << "--root-element-first" << endl
      << " Treat only the first global element as a document\n"
      << " root."
      << endl;

    e << "--root-element-last" << endl
      << " Treat only the last global element as a document\n"
      << " root."
      << endl;

    e << "--root-element-all" << endl
      << " Treat all global elements as document roots."
      << endl;

    e << "--root-element-none" << endl
      << " Don't treat any global elements as document roots."
      << endl;

    e << "--root-element <element>" << endl
      << " Treat only <element> as a document root. Repeat\n"
      << " this option to specify more than one root element."
      << endl;

    e << "--custom-type <map>" << endl
      << " Use a custom C++ type instead of the generated\n"
      << " class. The <map> argument is in the form\n"
      << " name[=type[/base]], where <name> is a type name as\n"
      << " defined in XML Schema, <type> is a C++ type name\n"
      << " that should be used instead, and optional <base>\n"
      << " is a C++ name that should be given to the C++\n"
      << " class generated from the XML Schema definition\n"
      << " which is normally used as a base for the custom\n"
      << " type."
      << endl;

    e << "--custom-type-regex <regex>" << endl
      << " Use custom C++ types instead of the generated\n"
      << " classes. The <regex> argument is in the form\n"
      << " /name/[type/[base/]], where <name> is a regex\n"
      << " pattern that will be matched against type names\n"
      << " as defined in XML Schema, <type> is a C++ type\n"
      << " name that should be used instead, and optional\n"
      << " <base> is a C++ name that should be given to\n"
      << " the C++ class generated from the XML Schema\n"
      << " definition."
      << 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 << "--fwd-suffix <suffix>" << endl
      << " Use <suffix> instead of the default '-fwd.hxx'\n"
      << " to construct the name of the forward declaration\n"
      << " 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;

    e << "--fwd-regex <regex>" << endl
      << " Use <regex> to construct the name of the forward\n"
      << " declaration 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 << "--fwd-prologue <text>" << endl
      << " Insert <text> at the beginning of the forward\n"
      << " declaration 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 << "--fwd-epilogue <text>" << endl
      << " Insert <text> at the end of the forward\n"
      << " declaration 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 << "--fwd-prologue-file <file>" << endl
      << " Insert the content of the <file> at the beginning\n"
      << " of the forward declaration 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 << "--fwd-epilogue-file <file>" << endl
      << " Insert the content of the <file> at the end of\n"
      << " the forward declaration 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 << "--parts <num>" << endl
      << " Split generated source code into <num> parts."
      << endl;

    e << "--parts-suffix <suffix>" << endl
      << " Use <suffix> instead of the default '-' to\n"
      << " separate the file name from the part number."
      << endl;

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

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

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

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

    spec.option<CLI::type_naming> ().default_value ("knr");
    spec.option<CLI::function_naming> ().default_value ("knr");

    spec.option<CLI::parts> ().default_value (1);
    spec.option<CLI::parts_suffix> ().default_value ("-");

    return spec;
  }


  namespace
  {
    Void
    open (WideInputFileStream& ifs, NarrowString const& path)
    {
      try
      {
        Path fs_path (path);
        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 Tree::Generator::Failed ();
        }
      }
      catch (InvalidPath const&)
      {
        wcerr << "error: '" << path.c_str () << "' is not a valid "
              << "filesystem path" << endl;

        throw Tree::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 Tree::Generator::
  generate (Tree::CLI::Options const& ops,
            Schema& schema,
            Path const& file_path,
            const WarningSet& disabled_warnings,
            FileList& file_list,
            AutoUnlinks& unlinks)
  {
    using std::ios_base;
    namespace Indentation = BackendElements::Indentation;

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

    using Cult::Containers::Vector;

    typedef Vector<Path> Paths;
    typedef Vector<Evptr<WideOutputFileStream> > WideOutputFileStreams;

    try
    {
      // Do option validation.
      //
      if (ops.value<CLI::parts> () < 1)
      {
        wcerr << "error: invalid value for option --parts: " <<
          ops.value<CLI::parts> () << endl;
        throw Failed ();
      }

      // Get counts.
      //
      Counts counts;
      {
        Counter counter;
        counts = counter.count (ops, schema);

        /*
        wcerr << "global type count: " << counts.global_types << endl;
        wcerr << "global element count: " << counts.global_elements << endl;
        wcerr << "generated global element count: " <<
          counts.generated_global_elements << endl;

        wcerr << "total complexity: " << counts.complexity_total << endl;
        wcerr << "complexity vector size: " << counts.complexity.size ()
              << endl;
        */
      }

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

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

      // Parts.
      //
      UnsignedLong parts (ops.value<CLI::parts> ());
      UnsignedLong units (
        counts.global_types + counts.generated_global_elements);

      UnsignedLong units_per_part (units / parts);

      if (parts != 1 && units_per_part < 1)
      {
        wcerr << "error: too many parts specified: " << parts << endl;
        throw Failed ();
      }

      UnsignedLong complexity_per_part (counts.complexity_total / parts);


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

      //
      //
      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.string () != name)
            generate_xml_schema = false;
        }
      }

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

      Boolean forward (ops.value<CLI::generate_forward> () &&
                       !generate_xml_schema);

      Boolean source (!generate_xml_schema);

      // Generate code.
      //
      NarrowString name (file_path.leaf ().string());

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

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

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

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

      Regex fwd_expr (ops.value <CLI::fwd_regex> ().empty ()
                      ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + fwd_suffix + "#"
                      : ops.value <CLI::fwd_regex> ());

      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 && parts == 1 && !cxx_expr.match (name))
      {
        wcerr << "error: source expression '" <<
          cxx_expr.pattern () << "' does not match '" <<
          name.c_str () << "'" << endl;
        throw Failed ();
      }

      if (forward && !fwd_expr.match (name))
      {
        wcerr << "error: forward expression '" <<
          fwd_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 fwd_name (forward ? fwd_expr.merge (name) : NarrowString ());

      Path hxx_path (hxx_name);
      Path ixx_path (ixx_name);
      Path fwd_path (fwd_name);
      Paths cxx_paths;

      if (source)
      {
        if (parts > 1)
        {
          for (UnsignedLong i (0); i < parts; ++i)
          {
            std::ostringstream os;
            os << i;

            Regex expr (
              "#^(.+?)(\\.[^./\\\\]+)?$#$1" + parts_suffix + os.str () + "$2#");

            NarrowString part_name (expr.merge (name));

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

            cxx_paths.push_back (
              Path (cxx_expr.merge (part_name)));
          }
        }
        else
          cxx_paths.push_back (
            Path (cxx_expr.merge (name)));
      }

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

          hxx_path = path / hxx_path;
          ixx_path = path / ixx_path;
          fwd_path = path / fwd_path;

          for (Paths::Iterator i (cxx_paths.begin ());
               i != cxx_paths.end (); ++i)
            *i = path / *i;
        }
        catch (InvalidPath const&)
        {
          wcerr << dir.c_str () << ": error: invalid path" << endl;
          throw Failed ();
        }
      }

      //
      //
      WideOutputFileStream hxx (hxx_path, ios_base::out);
      WideOutputFileStream ixx;
      WideOutputFileStream fwd;
      WideOutputFileStreams cxx;


      // FWD
      //
      if (forward)
      {
        fwd.open (fwd_path, ios_base::out);

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

        unlinks.add (fwd_path);
        file_list.push_back (fwd_path.string ());
      }


      // HXX
      //
      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.string ());


      // IXX
      //
      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.string ());
      }


      // CXX
      //
      if (source)
      {
        for (Paths::Iterator i (cxx_paths.begin ());
             i != cxx_paths.end (); ++i)
        {
          Evptr<WideOutputFileStream> s (
            new WideOutputFileStream (*i, ios_base::out));

          if (!s->is_open ())
          {
            wcerr << *i << ": error: unable to open in write mode" << endl;
            throw Failed ();
          }

          unlinks.add (*i);
          file_list.push_back (i->string ());
          cxx.push_back (s);
        }
      }


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

      if (forward)
        fwd << copyright;

      hxx << copyright;

      if (ops.value<CLI::generate_doxygen> ())
      {
        // Use native path format.
        //
        hxx << "/**" << endl
            << " * @file" << endl
            << " * @brief Generated from " << name.c_str () << "." << endl
            << " */" << endl
            << endl;

      }

      if (inline_)
        ixx << copyright;

      if (source)
      {
        for (WideOutputFileStreams::Iterator i (cxx.begin ());
             i != cxx.end (); ++i)
          **i << copyright;
      }


      // 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 ().string ();

      if (guard_prefix)
        guard_prefix += '_';

      // FWD
      //
      if (forward)
      {
        Context ctx (fwd, schema, ops, counts, generate_xml_schema,
                     &fwd_expr, &hxx_expr, &ixx_expr);

        Indentation::Clip<Indentation::SLOC, WideChar> fwd_sloc (fwd);

        // Guard
        //
        WideString guard (guard_expr.merge (guard_prefix + fwd_name));
        guard = ctx.escape (guard); // make a c++ id
        std::transform (guard.begin (), guard.end(), guard.begin (), upcase);

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

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

        append (fwd,
                ops.value<CLI::fwd_prologue> (),
                ops.value<CLI::prologue> ());
        append (fwd, ops.value<CLI::fwd_prologue_file> (), prologue);

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

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

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

          if (ctx.char_type == L"char")
          {
            fwd << "#ifndef XSD_USE_CHAR" << endl
                << "#define XSD_USE_CHAR" << endl
                << "#endif" << endl
                << endl;

            fwd << "#ifndef XSD_CXX_TREE_USE_CHAR" << endl
                << "#define XSD_CXX_TREE_USE_CHAR" << endl
                << "#endif" << endl
                << endl;
          }
          else if (ctx.char_type == L"wchar_t")
          {
            fwd << "#ifndef XSD_USE_WCHAR" << endl
                << "#define XSD_USE_WCHAR" << endl
                << "#endif" << endl
                << endl;

            fwd << "#ifndef XSD_CXX_TREE_USE_WCHAR" << endl
                << "#define XSD_CXX_TREE_USE_WCHAR" << endl
                << "#endif" << endl
                << endl;
          }

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


          // Generate.
          //
          generate_forward (ctx);

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

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

        append (fwd, ops.value<CLI::fwd_epilogue_file> (), epilogue);
        append (fwd,
                ops.value<CLI::fwd_epilogue> (),
                ops.value<CLI::epilogue> ());

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

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

        if (show_sloc)
        {
          wcerr << fwd_path << ": "
                << fwd_sloc.buffer ().count () << endl;

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

      // HXX
      //
      {
        Context ctx (hxx, schema, ops, counts, generate_xml_schema,
                     &fwd_expr, &hxx_expr, &ixx_expr);

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

        // Guard
        //
        WideString guard (guard_expr.merge (guard_prefix + hxx_name));
        guard = ctx.escape (guard); // make 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;

          // Generate character selection defines.
          //
          if (!forward)
          {
            if (ctx.char_type == L"char")
            {
              hxx << "#ifndef XSD_USE_CHAR" << endl
                  << "#define XSD_USE_CHAR" << endl
                  << "#endif" << endl
                  << endl;

              hxx << "#ifndef XSD_CXX_TREE_USE_CHAR" << endl
                  << "#define XSD_CXX_TREE_USE_CHAR" << endl
                  << "#endif" << endl
                  << endl;
            }
            else if (ctx.char_type == L"wchar_t")
            {
              hxx << "#ifndef XSD_USE_WCHAR" << endl
                  << "#define XSD_USE_WCHAR" << endl
                  << "#endif" << endl
                  << endl;

              hxx << "#ifndef XSD_CXX_TREE_USE_WCHAR" << endl
                  << "#define XSD_CXX_TREE_USE_WCHAR" << endl
                  << "#endif" << endl
                  << endl;
            }
          }

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


          // Generate.
          //
          if (!generate_xml_schema)
          {
            if (forward)
              hxx << "#include " << ctx.process_include_path (fwd_name)
                  << endl << endl;
            else
              generate_forward (ctx);
          }

          generate_tree_header (ctx);

          if (!generate_xml_schema)
          {

            if (ops.value<CLI::generate_ostream> ())
              generate_stream_header (ctx);

            if (!ops.value<CLI::suppress_parsing> ())
              generate_parser_header (ctx);

            if (ops.value<CLI::generate_serialization> ())
              generate_serialization_header (ctx);

            if (!ops.value<CLI::generate_insertion> ().empty ())
              generate_stream_insertion_header (ctx);
          }

          if (inline_)
          {
            hxx << "#ifndef XSD_DONT_INCLUDE_INLINE" << endl
                << "#include " << ctx.process_include_path (ixx_name) << endl
                << "#endif // XSD_DONT_INCLUDE_INLINE" << endl
                << 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, counts, generate_xml_schema,
                     &fwd_expr, &hxx_expr, &ixx_expr);

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

        // Guard
        //
        WideString guard (guard_expr.merge (guard_prefix + ixx_name));
        guard = ctx.escape (guard); // make a c++ id
        std::transform (guard.begin (), guard.end(), guard.begin (), upcase);

        ixx << "#ifndef " << guard.c_str () << endl
            << "#define " << guard.c_str () << endl
            << endl;

        // 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_tree_inline (ctx, 1, 0);
        }

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

        ixx << "#endif // " << guard.c_str () << endl;

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

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


      // CXX
      //

      if (source)
      {
        UnsignedLong first_unit (0); // First unit in the current part.

        for (UnsignedLong part (0); part < parts; ++part)
        {
          // Figure out the range of units for this part.
          //
          UnsignedLong last_unit (first_unit);

          if (units != 0)
          {
            UnsignedLong complexity (counts.complexity[last_unit]);

            while (complexity < complexity_per_part)
            {
              // Make sure there will be at least one unit for each part left.
              //
              if ((last_unit + 1) >= units ||
                  (units - (last_unit + 1) - 1) < (parts - part - 1))
                break;

              // Check if the increase in complexity should be kept in this
              // part or moved to the next.
              //
              UnsignedLong new_complexity (
                complexity + counts.complexity[last_unit + 1]);

              if (new_complexity > complexity_per_part)
              {
                if ((new_complexity - complexity_per_part) >
                    (counts.complexity[last_unit + 1] / 2))
                  break;
              }

              last_unit++;
              complexity = new_complexity;
            }

            if (part + 1 == parts)
            {
              // Last part.
              //
              last_unit = units - 1;
            }
          }

          //
          //
          UnsignedLong first (first_unit);
          UnsignedLong last (last_unit);

          first_unit = last_unit + 1;

          //wcerr << "[" << first << ", " << last << "]: " << complexity
          //      << endl;

          WideOutputFileStream& os (*cxx[part]);

          Context ctx (os, schema, ops, counts, generate_xml_schema,
                       &fwd_expr, &hxx_expr, &ixx_expr);

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

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

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

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

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

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


            // Generate.
            //
            os << "#include " << ctx.process_include_path (hxx_name) << endl
               << endl;

            if (!inline_)
              generate_tree_inline (ctx, first, last);

            generate_tree_source (ctx, first, last);

            if (ops.value<CLI::generate_ostream> ())
              generate_stream_source (ctx, first, last);

            if (!ops.value<CLI::suppress_parsing> ())
              generate_parser_source (ctx, first, last);

            if (ops.value<CLI::generate_serialization> ())
              generate_serialization_source (ctx, first, last);

            if (!ops.value<CLI::generate_extraction> ().empty ())
              generate_stream_extraction_source (ctx);

            if (!ops.value<CLI::generate_insertion> ().empty ())
              generate_stream_insertion_source (ctx);

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

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

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

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

          if (show_sloc)
          {
            wcerr << cxx_paths[part] << ": "
                  << cxx_sloc.buffer ().count () << endl;

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

      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 (InvalidCustomTypeMapping const& e)
    {
      wcerr << "error: invalid custom type 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 ();
    }
  }
}
