// file : xsde/cxx/parser/generator.cxx // author : Boris Kolpackov // copyright : Copyright (c) 2005-2011 Code Synthesis Tools CC // license : GNU GPL v2 + exceptions; see accompanying LICENSE file #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../../libxsde/xsde/cxx/version.hxx" using std::endl; using std::wcerr; using std::wcout; 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-2011 Code Synthesis Tools CC\n" "//\n" "// This program was generated by CodeSynthesis XSD/e, an XML Schema\n" "// to C++ data binding compiler for embedded systems.\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" "//\n\n"; Char const copyright_proprietary[] = "// Copyright (c) 2005-2011 Code Synthesis Tools CC\n" "//\n" "// This program was generated by CodeSynthesis XSD/e, an XML Schema to\n" "// C++ data binding compiler for embedded systems, in the Proprietary\n" "// License mode. You should have received a proprietary license from\n" "// Code Synthesis Tools CC prior to generating this code. See the\n" "// license text for conditions.\n" "//\n\n"; Char const copyright_impl[] = "// Not copyrighted - public domain.\n" "//\n" "// This sample parser implementation was generated by CodeSynthesis XSD/e,\n" "// an XML Schema to C++ data binding compiler for embedded systems. You\n" "// may use it in your programs without any restrictions.\n" "//\n\n"; } namespace Parser { namespace CLI { extern Key type_map = "type-map"; extern Key char_encoding = "char-encoding"; extern Key no_stl = "no-stl"; extern Key no_iostream = "no-iostream"; extern Key no_exceptions = "no-exceptions"; extern Key no_long_long = "no-long-long"; extern Key reuse_style_mixin = "reuse-style-mixin"; extern Key reuse_style_none = "reuse-style-none"; extern Key generate_inline = "generate-inline"; extern Key suppress_validation = "suppress-validation"; extern Key generate_polymorphic = "generate-polymorphic"; extern Key runtime_polymorphic = "runtime-polymorphic"; extern Key suppress_reset = "suppress-reset"; extern Key custom_allocator = "custom-allocator"; 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 output_dir = "output-dir"; 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 show_anonymous = "show-anonymous"; extern Key show_sloc = "show-sloc"; extern Key proprietary_license = "proprietary-license"; } } Void Parser::Generator:: usage () { std::wostream& o (wcout); ::CLI::Indent::Clip< ::CLI::OptionsUsage, WideChar> clip (o); o << "--type-map " << endl << " Read XML Schema to C++ type mapping information\n" << " from . 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; o << "--char-encoding " << endl << " Specify the character encoding that should be\n" << " used for the extracted text data. Valid values\n" << " are 'utf8' (default) and 'iso8859-1'." << endl; o << "--no-stl" << endl << " Generate code that does not use STL." << endl; o << "--no-iostream" << endl << " Generate code that does not use the iostream\n" << " library." << endl; o << "--no-exceptions" << endl << " Generate code that does not use C++ exceptions." << endl; o << "--no-long-long" << endl << " Generate code that does not use the long long\n" << " and unsigned long long types." << endl; o << "--reuse-style-mixin" << endl << " Generate code that supports the mixin base parser\n" << " implementation reuse style." << endl; o << "--reuse-style-none" << endl << " Do not generate any support for base parser\n" << " implementation reuse." << endl; o << "--generate-inline" << endl << " Generate certain functions inline." << endl; o << "--suppress-validation" << endl << " Suppress the generation of validation code." << endl; o << "--generate-polymorphic" << endl << " Generate polymorphism-aware code. Specify this\n" << " option if you use substitution groups or xsi:type." << endl; o << "--runtime-polymorphic" << endl << " Generate non-polymorphic code that uses the\n" << " runtime library configured with polymorphism\n" << " support." << endl; o << "--suppress-reset" << endl << " Suppress the generation of parser reset code." << endl; o << "--custom-allocator" << endl << " Generate code that uses custom allocator functions\n" << " instead of operator new/delete." << endl; o << "--generate-noop-impl" << endl << " Generate a sample parser implementation that\n" << " does nothing (no operation)." << endl; o << "--generate-print-impl" << endl << " Generate a sample parser implementation that\n" << " prints the XML data to STDOUT." << endl; o << "--generate-test-driver" << endl << " Generate a test driver for the sample parser\n" << " implementation." << endl; o << "--force-overwrite" << endl << " Force overwriting of the existing implementation\n" << " and test driver files." << endl; o << "--root-element-first" << endl << " Indicate that the first global element is the\n" << " document root." << endl; o << "--root-element-last" << endl << " Indicate that the last global element is the\n" << " document root." << endl; o << "--root-element " << endl << " Indicate that is the document root." << endl; o << "--generate-xml-schema" << endl << " Generate a C++ header file as if the schema being\n" << " compiled defines the XML Schema namespace." << endl; o << "--extern-xml-schema " << endl << " Generate code as if the XML Schema namespace was\n" << " defined in and xsd:included in the schema\n" << " being compiled." << endl; o << "--output-dir " << endl << " Write generated files to instead of the\n" << " current directory." << endl; o << "--skel-type-suffix " << endl << " Use instead of the default '_pskel' to\n" << " construct the names of generated parser skeletons." << endl; o << "--skel-file-suffix " << endl << " Use instead of the default '-pskel' to\n" << " construct the names of generated parser skeleton\n" << " files." << endl; o << "--impl-type-suffix " << endl << " Use 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; o << "--impl-file-suffix " << endl << " Use instead of the default '-pimpl' to\n" << " construct the names of generated sample parser\n" << " implementation files." << endl; o << "--namespace-map =" << endl << " Map XML Schema namespace to C++ namespace\n" << " . Repeat this option to specify mapping for\n" << " more than one XML Schema namespace." << endl; o << "--namespace-regex " << endl << " Add to the list of regular expressions\n" << " used to translate XML Schema namespace names to\n" << " C++ namespace names." << endl; o << "--namespace-regex-trace" << endl << " Trace the process of applying regular expressions\n" << " specified with the --namespace-regex option." << endl; o << "--reserved-name " << endl << " Add 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; o << "--include-with-brackets" << endl << " Use angle brackets (<>) instead of quotes (\"\") in\n" << " generated #include directives." << endl; o << "--include-prefix " << endl << " Add to generated #include directive\n" << " paths." << endl; o << "--include-regex " << endl << " Add to the list of regular expressions\n" << " used to transform #include directive paths." << endl; o << "--include-regex-trace" << endl << " Trace the process of applying regular expressions\n" << " specified with the --include-regex option." << endl; o << "--guard-prefix " << endl << " Add to generated header inclusion guards." << endl; o << "--hxx-suffix " << endl << " Use instead of the default '.hxx' to\n" << " construct the name of the header file." << endl; o << "--ixx-suffix " << endl << " Use instead of the default '.ixx' to\n" << " construct the name of the inline file." << endl; o << "--cxx-suffix " << endl << " Use instead of the default '.cxx' to\n" << " construct the name of the source file." << endl; o << "--hxx-regex " << endl << " Use to construct the name of the header\n" << " file." << endl; o << "--ixx-regex " << endl << " Use to construct the name of the inline\n" << " file." << endl; o << "--cxx-regex " << endl << " Use to construct the name of the source\n" << " file." << endl; // Prologues. // o << "--hxx-prologue " << endl << " Insert at the beginning of the header file." << endl; o << "--ixx-prologue " << endl << " Insert at the beginning of the inline file." << endl; o << "--cxx-prologue " << endl << " Insert at the beginning of the source file." << endl; o << "--prologue " << endl << " Insert at the beginning of each generated\n" << " file for which there is no file-specific prologue." << endl; // Epilogues. // o << "--hxx-epilogue " << endl << " Insert at the end of the header file." << endl; o << "--ixx-epilogue " << endl << " Insert at the end of the inline file." << endl; o << "--cxx-epilogue " << endl << " Insert at the end of the source file." << endl; o << "--epilogue " << endl << " Insert at the end of each generated file\n" << " for which there is no file-specific epilogue." << endl; // Prologue files. // o << "--hxx-prologue-file " << endl << " Insert the content of the at the beginning\n" << " of the header file." << endl; o << "--ixx-prologue-file " << endl << " Insert the content of the at the beginning\n" << " of the inline file." << endl; o << "--cxx-prologue-file " << endl << " Insert the content of the at the beginning\n" << " of the source file." << endl; o << "--prologue-file " << endl << " Insert the content of the at the beginning\n" << " of each generated file for which there is no file-\n" << " specific prologue file." << endl; // Epilogue files. // o << "--hxx-epilogue-file " << endl << " Insert the content of the at the end of\n" << " the header file." << endl; o << "--ixx-epilogue-file " << endl << " Insert the content of the at the end of\n" << " the inline file." << endl; o << "--cxx-epilogue-file " << endl << " Insert the content of the at the end of\n" << " the source file." << endl; o << "--epilogue-file " << endl << " Insert the content of the at the end of\n" << " each generated file for which there is no file-\n" << " specific epilogue file." << endl; // Misc. // o << "--show-anonymous" << endl << " Show elements and attributes that are of anonymous\n" << " types." << endl; o << "--show-sloc" << endl << " Show the number of generated physical source lines\n" << " of code (SLOC)." << endl; o << "--sloc-limit " << endl << " Check that the number of generated physical source\n" << " lines of code (SLOC) does not exceed ." << endl; o << "--options-file " << endl << " Read additional options from . Each option\n" << " should appear on a separate line optionally\n" << " followed by space and an argument." << endl; o << "--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 ().default_value ("utf8"); spec.option ().default_value ("-pskel"); spec.option ().default_value ("_pskel"); spec.option ().default_value ("-pimpl"); spec.option ().default_value ("_pimpl"); spec.option ().default_value (".hxx"); spec.option ().default_value (".ixx"); spec.option ().default_value (".cxx"); return spec; } Void Parser::Generator:: process_names (CLI::Options const& ops, XSDFrontend::SemanticGraph::Schema& schema, XSDFrontend::SemanticGraph::Path const& file) { NameProcessor proc; proc.process (ops, schema, file, false); } namespace { template 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 const& primary, Cult::Containers::Vector const& def) { Cult::Containers::Vector const& v ( primary.empty () ? def : primary); for (Containers::Vector::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 fpt, TypeMap::Namespaces& type_map, Boolean gen_driver, const WarningSet& disabled_warnings, FileList& file_list, AutoUnlinks& unlinks) { using std::ios_base; namespace Indentation = BackendElements::Indentation; typedef cutl::re::regexsub Regex; try { Boolean generate_xml_schema (ops.value ()); // 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 ()) { if (file_path.native_file_string () != name) generate_xml_schema = false; } } Boolean impl (!generate_xml_schema && (ops.value () || ops.value ())); Boolean driver (gen_driver && !generate_xml_schema && ops.value ()); // 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, true); } Boolean validation (!ops.value ()); // Compute state machine info. // if (validation) { StateProcessor proc; proc.process (schema, file_path); } // Read-in type maps. // { using namespace TypeMap; typedef Containers::Vector Files; Files const& files (ops.value ()); 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 xns; { Context ctx (std::wcerr, schema, file_path, ops, 0, 0, 0); xns = ctx.xs_ns_name (); } if (ops.value ()) { TypeMap::Namespace xsd_std ("http://www\\.w3\\.org/2001/XMLSchema"); String qname (xns + L"::qname*"); String string_seq (xns + L"::string_sequence*"); xsd_std.types_push_back ("anySimpleType", "char*", "char*"); xsd_std.types_push_back ("string", "char*", "char*"); xsd_std.types_push_back ("normalizedString", "char*", "char*"); xsd_std.types_push_back ("token", "char*", "char*"); xsd_std.types_push_back ("Name", "char*", "char*"); xsd_std.types_push_back ("NMTOKEN", "char*", "char*"); xsd_std.types_push_back ("NMTOKENS", string_seq, string_seq); xsd_std.types_push_back ("NCName", "char*", "char*"); xsd_std.types_push_back ("ID", "char*", "char*"); xsd_std.types_push_back ("IDREF", "char*", "char*"); xsd_std.types_push_back ("IDREFS", string_seq, string_seq); xsd_std.types_push_back ("language", "char*", "char*"); xsd_std.types_push_back ("anyURI", "char*", "char*"); xsd_std.types_push_back ("QName", qname, qname); type_map.push_back (xsd_std); } else { TypeMap::Namespace xsd_std ("http://www\\.w3\\.org/2001/XMLSchema"); String qname (xns + L"::qname"); String string_seq (xns + L"::string_sequence*"); xsd_std.types_push_back ("anySimpleType", "::std::string"); xsd_std.types_push_back ("string", "::std::string"); xsd_std.types_push_back ("normalizedString", "::std::string"); xsd_std.types_push_back ("token", "::std::string"); xsd_std.types_push_back ("Name", "::std::string"); xsd_std.types_push_back ("NMTOKEN", "::std::string"); xsd_std.types_push_back ("NMTOKENS", string_seq, string_seq); xsd_std.types_push_back ("NCName", "::std::string"); xsd_std.types_push_back ("ID", "::std::string"); xsd_std.types_push_back ("IDREF", "::std::string"); xsd_std.types_push_back ("IDREFS", string_seq, string_seq); xsd_std.types_push_back ("language", "::std::string"); xsd_std.types_push_back ("anyURI", "::std::string"); xsd_std.types_push_back ("QName", qname); type_map.push_back (xsd_std); } String buffer (xns + L"::buffer*"); TypeMap::Namespace xsd ("http://www\\.w3\\.org/2001/XMLSchema"); 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"); if (ops.value ()) { xsd.types_push_back ("long", "long", "long"); xsd.types_push_back ("unsignedLong", "unsigned long", "unsigned long"); } else { 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"); xsd.types_push_back ("negativeInteger", "long", "long"); xsd.types_push_back ("nonPositiveInteger", "long", "long"); xsd.types_push_back ("positiveInteger", "unsigned long", "unsigned long"); xsd.types_push_back ("nonNegativeInteger", "unsigned long", "unsigned long"); xsd.types_push_back ("float", "float", "float"); xsd.types_push_back ("double", "double", "double"); xsd.types_push_back ("decimal", "double", "double"); 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"); 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, type_map); } // Generate code. // Boolean inline_ (ops.value () && !generate_xml_schema); Boolean source (!generate_xml_schema); NarrowString name (file_path.leaf ()); NarrowString skel_suffix (ops.value ()); NarrowString impl_suffix (ops.value ()); NarrowString hxx_suffix (ops.value ()); NarrowString ixx_suffix (ops.value ()); NarrowString cxx_suffix (ops.value ()); Regex hxx_expr ( ops.value ().empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + hxx_suffix + "#" : ops.value ()); Regex ixx_expr ( ops.value ().empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + ixx_suffix + "#" : ops.value ()); Regex cxx_expr ( ops.value ().empty () ? "#^(.+?)(\\.[^./\\\\]+)?$#$1" + skel_suffix + cxx_suffix + "#" : ops.value ()); 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-pdriver" + cxx_suffix + "#"; } if (!hxx_expr.match (name)) { wcerr << "error: header expression '" << hxx_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } if (inline_ && !ixx_expr.match (name)) { wcerr << "error: inline expression '" << ixx_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } if (source && !cxx_expr.match (name)) { wcerr << "error: source expression '" << cxx_expr.regex ().str ().c_str () << "' 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.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } if (!cxx_impl_expr.match (name)) { wcerr << "error: implementation source expression '" << cxx_impl_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } if (!cxx_driver_expr.match (name)) { wcerr << "error: driver source expression '" << cxx_driver_expr.regex ().str ().c_str () << "' does not match '" << name.c_str () << "'" << endl; throw Failed (); } } NarrowString hxx_name (hxx_expr.replace (name)); NarrowString ixx_name (inline_ ? ixx_expr.replace (name) : NarrowString ()); NarrowString cxx_name (source ? cxx_expr.replace (name) : NarrowString ()); NarrowString hxx_impl_name; NarrowString cxx_impl_name; NarrowString cxx_driver_name; if (impl || driver) { hxx_impl_name = hxx_impl_expr.replace (name); cxx_impl_name = cxx_impl_expr.replace (name); cxx_driver_name = cxx_driver_expr.replace (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); } Path out_dir; if (NarrowString dir = ops.value ()) { try { out_dir = Path (dir, boost::filesystem::native); } catch (InvalidPath const&) { wcerr << dir.c_str () << ": error: invalid path" << endl; throw Failed (); } } if (fpt && !generate_xml_schema) { // In the file-per-type mode the schema files are always local // unless the user added the directory so that we propagate this // to the output files. // Path fpt_dir (file_path.branch_path ()); if (!fpt_dir.empty ()) out_dir /= fpt_dir; } if (!out_dir.empty ()) { hxx_path = out_dir / hxx_path; ixx_path = out_dir / ixx_path; cxx_path = out_dir / cxx_path; if (impl || driver) { hxx_impl_path = out_dir / hxx_impl_path; cxx_impl_path = out_dir / cxx_impl_path; cxx_driver_path = out_dir /cxx_driver_path; } } // 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 ()) { 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 ()) { 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 ()) { 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 () ? 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 ()); if (name) open (prologue, name); } // Epilogue. // WideInputFileStream epilogue; { NarrowString name (ops.value ()); if (name) open (epilogue, name); } // SLOC counter. // UnsignedLong sloc_total (0); Boolean show_sloc (ops.value ()); typedef compiler::ostream_filter ind_filter; typedef compiler::ostream_filter sloc_filter; // // Regex guard_expr ("/([a-z])([A-Z])/$1_$2/"); // Split words. NarrowString guard_prefix (ops.value ()); if (!guard_prefix) guard_prefix = file_path.branch_path ().native_directory_string (); if (guard_prefix) guard_prefix += '_'; // HXX // { Context ctx ( hxx, schema, file_path, ops, &hxx_expr, &ixx_expr, &hxx_impl_expr); sloc_filter sloc (hxx); String guard (guard_expr.replace (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; // Version check. // hxx << "#include " << endl << endl << "#if (XSDE_INT_VERSION != " << XSDE_INT_VERSION << "L)" << endl << "#error XSD/e runtime version mismatch" << endl << "#endif" << endl << endl; // Runtime/generated code compatibility checks. // hxx << "#include " << endl << endl; if (ops.value () == "iso8859-1") { hxx << "#ifndef XSDE_ENCODING_ISO8859_1" << endl << "#error the generated code uses the ISO-8859-1 encoding" << "while the XSD/e runtime does not (reconfigure the runtime " << "or change the --char-encoding value)" << endl << "#endif" << endl << endl; } else { hxx << "#ifndef XSDE_ENCODING_UTF8" << endl << "#error the generated code uses the UTF-8 encoding" << "while the XSD/e runtime does not (reconfigure the runtime " << "or change the --char-encoding value)" << endl << "#endif" << endl << endl; } if (ops.value ()) { hxx << "#ifdef XSDE_STL" << endl << "#error the XSD/e runtime uses STL while the " << "generated code does not (reconfigure the runtime or " << "remove --no-stl)" << endl << "#endif" << endl << endl; } else { hxx << "#ifndef XSDE_STL" << endl << "#error the generated code uses STL while the " << "XSD/e runtime does not (reconfigure the runtime or " << "add --no-stl)" << endl << "#endif" << endl << endl; } if (ops.value ()) { hxx << "#ifdef XSDE_IOSTREAM" << endl << "#error the XSD/e runtime uses iostream while the " << "generated code does not (reconfigure the runtime or " << "remove --no-iostream)" << endl << "#endif" << endl << endl; } else { hxx << "#ifndef XSDE_IOSTREAM" << endl << "#error the generated code uses iostream while the " << "XSD/e runtime does not (reconfigure the runtime or " << "add --no-iostream)" << endl << "#endif" << endl << endl; } if (ops.value ()) { hxx << "#ifdef XSDE_EXCEPTIONS" << endl << "#error the XSD/e runtime uses exceptions while the " << "generated code does not (reconfigure the runtime or " << "remove --no-exceptions)" << endl << "#endif" << endl << endl; } else { hxx << "#ifndef XSDE_EXCEPTIONS" << endl << "#error the generated code uses exceptions while the " << "XSD/e runtime does not (reconfigure the runtime or " << "add --no-exceptions)" << endl << "#endif" << endl << endl; } if (ops.value ()) { hxx << "#ifdef XSDE_LONGLONG" << endl << "#error the XSD/e runtime uses long long while the " << "generated code does not (reconfigure the runtime or " << "remove --no-long-long)" << endl << "#endif" << endl << endl; } else { hxx << "#ifndef XSDE_LONGLONG" << endl << "#error the generated code uses long long while the " << "XSD/e runtime does not (reconfigure the runtime or " << "add --no-long-long)" << endl << "#endif" << endl << endl; } if (ops.value ()) { hxx << "#ifdef XSDE_PARSER_VALIDATION" << endl << "#error the XSD/e runtime uses validation while the " << "generated code does not (reconfigure the runtime or " << "remove --suppress-validation)" << endl << "#endif" << endl << endl; } else { hxx << "#ifndef XSDE_PARSER_VALIDATION" << endl << "#error the generated code uses validation while the " << "XSD/e runtime does not (reconfigure the runtime or " << "add --suppress-validation)" << endl << "#endif" << endl << endl; } if (ops.value () || ops.value ()) { hxx << "#ifndef XSDE_POLYMORPHIC" << endl << "#error the generated code expects XSD/e runtime with " << "polymorphism support (reconfigure the runtime or remove " << "--generate-polymorphic/--runtime-polymorphic)" << endl << "#endif" << endl << endl; } else { hxx << "#ifdef XSDE_POLYMORPHIC" << endl << "#error the generated code expects XSD/e runtime " << "without polymorphism support (reconfigure the runtime or " << "add --generate-polymorphic/--runtime-polymorphic)" << endl << "#endif" << endl << endl; } if (ops.value ()) { hxx << "#ifndef XSDE_REUSE_STYLE_MIXIN" << endl << "#error the generated code uses the mixin reuse style " << "while the XSD/e runtime does not (reconfigure the runtime " << "or remove --reuse-style-mixin)" << endl << "#endif" << endl << endl; } else if (ops.value ()) { hxx << "#ifndef XSDE_REUSE_STYLE_NONE" << endl << "#error the generated code does not provide support " << "for parser reuse while the XSD/e runtime does (reconfigure " << "the runtime or remove --reuse-style-none)" << endl << "#endif" << endl << endl; } else { hxx << "#ifndef XSDE_REUSE_STYLE_TIEIN" << endl << "#error the generated code uses the tiein reuse style " << "while the XSD/e runtime does not (reconfigure the runtime " << "or add --reuse-style-mixin or --reuse-style-none)" << endl << "#endif" << endl << endl; } if (ops.value ()) { hxx << "#ifndef XSDE_CUSTOM_ALLOCATOR" << endl << "#error the generated code uses custom allocator while " << "the XSD/e runtime does not (reconfigure the runtime or " << "remove --custom-allocator)" << endl << "#endif" << endl << endl; } else { hxx << "#ifdef XSDE_CUSTOM_ALLOCATOR" << endl << "#error the XSD/e runtime uses custom allocator while " << "the generated code does not (reconfigure the runtime or " << "add --custom-allocator)" << endl << "#endif" << endl << endl; } // // hxx << "#include " << endl << endl; // Copy prologue. // hxx << "// Begin prologue." << endl << "//" << endl; append ( hxx, ops.value (), ops.value ()); append (hxx, ops.value (), prologue); hxx << "//" << endl << "// End prologue." << endl << endl; // Generate. // { ind_filter ind (hxx); // We don't want to indent prologues/epilogues. 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 << endl; // Copy epilogue. // hxx << "// Begin epilogue." << endl << "//" << endl; append (hxx, ops.value (), epilogue); append ( hxx, ops.value (), ops.value ()); hxx << "//" << endl << "// End epilogue." << endl << endl; hxx << "#include " << endl << endl; hxx << "#endif // " << guard << endl; if (show_sloc) wcerr << hxx_path << ": " << sloc.stream ().count () << endl; sloc_total += sloc.stream ().count (); } // IXX // if (inline_) { Context ctx ( ixx, schema, file_path, ops, &hxx_expr, &ixx_expr, &hxx_impl_expr); sloc_filter sloc (ixx); // Copy prologue. // ixx << "// Begin prologue." << endl << "//" << endl; append ( ixx, ops.value (), ops.value ()); append (ixx, ops.value (), prologue); ixx << "//" << endl << "// End prologue." << endl << endl; // Generate. // { ind_filter ind (ixx); // We don't want to indent prologues/epilogues. generate_parser_inline (ctx); } // Copy epilogue. // ixx << "// Begin epilogue." << endl << "//" << endl; append (ixx, ops.value (), epilogue); append ( ixx, ops.value (), ops.value ()); ixx << "//" << endl << "// End epilogue." << endl << endl; if (show_sloc) wcerr << ixx_path << ": " << sloc.stream ().count () << endl; sloc_total += sloc.stream ().count (); } // CXX // if (source) { Context ctx ( cxx, schema, file_path, ops, &hxx_expr, &ixx_expr, &hxx_impl_expr); sloc_filter sloc (cxx); // Copy prologue. // cxx << "// Begin prologue." << endl << "//" << endl; append ( cxx, ops.value (), ops.value ()); append (cxx, ops.value (), prologue); cxx << "//" << endl << "// End prologue." << endl << endl; cxx << "#include " << ctx.process_include_path (hxx_name) << endl << endl; // Generate. // { ind_filter ind (cxx); // We don't want to indent prologues/epilogues. 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 (), epilogue); append ( cxx, ops.value (), ops.value ()); cxx << "//" << endl << "// End epilogue." << endl << endl; if (show_sloc) wcerr << cxx_path << ": " << sloc.stream ().count () << endl; sloc_total += sloc.stream ().count (); } // HXX impl // if (impl) { Context ctx (hxx_impl, schema, file_path, ops, &hxx_expr, &ixx_expr, &hxx_impl_expr); String guard (guard_expr.replace (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; hxx_impl << "#include " << ctx.process_include_path (hxx_name) << endl << endl; { ind_filter ind (hxx_impl); generate_impl_header (ctx); } hxx_impl << "#endif // " << guard << endl; } // CXX impl // if (impl) { Context ctx (cxx_impl, schema, file_path, ops, &hxx_expr, &ixx_expr, &hxx_impl_expr); cxx_impl << "#include " << ctx.process_include_path (hxx_impl_name) << endl << endl; { ind_filter ind (cxx_impl); generate_impl_source (ctx); } } // CXX driver // if (driver) { Context ctx (cxx_driver, schema, file_path, ops, &hxx_expr, &ixx_expr, &hxx_impl_expr); cxx_driver << "#include " << ctx.process_include_path (hxx_impl_name) << endl << endl; { ind_filter ind (cxx_driver); generate_driver_source (ctx); } } return sloc_total; } catch (UnrepresentableCharacter const& e) { wcerr << "error: character at position " << e.position () << " " << "in string '" << e.string () << "' is unrepresentable in " << "the target encoding" << endl; throw Failed (); } 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 (cutl::re::format const& e) { wcerr << "error: invalid regex: '" << e.regex ().c_str () << "': " << e.description ().c_str () << endl; throw Failed (); } catch (cutl::re::wformat const& e) { wcerr << "error: invalid regex: '" << e.regex () << "': " << e.description ().c_str () << endl; throw Failed (); } } }