// file : xsd/cxx/tree/validator.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 using namespace std; namespace CXX { namespace Tree { namespace { class ValidationContext: public Context { public: ValidationContext (SemanticGraph::Schema& root, SemanticGraph::Path const& path, Tree::options const& ops, const WarningSet& disabled_warnings, Counts const& counts, bool generate_xml_schema, bool& valid_) : Context (std::wcerr, root, path, ops, counts, generate_xml_schema, 0, 0, 0, 0), disabled_warnings_ (disabled_warnings), disabled_warnings_all_ (false), valid (valid_), subst_group_warning_issued (subst_group_warning_issued_), subst_group_warning_issued_ (false) { if (disabled_warnings_.find ("all") != disabled_warnings_.end ()) disabled_warnings_all_ = true; } public: bool is_disabled (char const* w) { return disabled_warnings_all_ || disabled_warnings_.find (w) != disabled_warnings_.end (); } public: String xpath (SemanticGraph::Nameable& n) { if (n.is_a ()) return L""; // There is a bug if you see this. if (n.named_p ()) { SemanticGraph::Scope& scope (n.scope ()); if (scope.is_a ()) return n.name (); return xpath (scope) + L"/" + n.name (); } else { return L"(anonymous type for " + n.context ().get ("instance-name") + L")"; } } protected: ValidationContext (ValidationContext& c) : Context (c), disabled_warnings_ (c.disabled_warnings_), disabled_warnings_all_ (c.disabled_warnings_all_), valid (c.valid), subst_group_warning_issued (c.subst_group_warning_issued) { } protected: const WarningSet& disabled_warnings_; bool disabled_warnings_all_; bool& valid; bool& subst_group_warning_issued; bool subst_group_warning_issued_; }; // // struct Any: Traversal::Any, ValidationContext { Any (ValidationContext& c) : ValidationContext (c) { } struct Element: Traversal::Element, ValidationContext { Element (ValidationContext& c, SemanticGraph::Any& any) : ValidationContext (c), any_ (any), ns_ (any.definition_namespace ().name ()) { } virtual void traverse (SemanticGraph::Element& e) { if (skip (e)) return; using SemanticGraph::Any; bool q (e.qualified_p ()); String ns (q ? e.namespace_ ().name () : ""); for (Any::NamespaceIterator i (any_.namespace_begin ()); i != any_.namespace_end (); ++i) { bool failed (false); if (*i == L"##any") { failed = true; } else if (*i == L"##other") { if (ns_) { // Note that here I assume that ##other does not // include names without target namespace. This // is not what the spec says but that seems to be // the consensus. // failed = q && ns != ns_; } else { // No target namespace. // failed = q && ns != L""; } } else if (*i == L"##local") { failed = !q || ns == L""; } else if (*i == L"##targetNamespace") { failed = (q && ns_ == ns) || (!q && ns_ == L""); } else { failed = q && *i == ns; } if (failed) { Any& a (any_); os << a.file () << ":" << a.line () << ":" << a.column () << ": warning T001: namespace '" << *i << "' allows for " << "element '" << e.name () << "'" << endl; os << a.file () << ":" << a.line () << ":" << a.column () << ": warning T001: generated code may not associate element '" << e.name () << "' correctly if it appears in place of " << "this wildcard" << endl; os << e.file () << ":" << e.line () << ":" << e.column () << ": info: element '" << e.name () << "' is defined " << "here" << endl; } } } private: SemanticGraph::Any& any_; String ns_; }; struct Complex: Traversal::Complex { Complex () : up_ (true), down_ (true) { } virtual void post (Type& c) { // Go down the inheritance hierarchy. // if (down_) { bool up = up_; up_ = false; if (c.inherits_p ()) dispatch (c.inherits ().base ()); up_ = up; } // Go up the inheritance hierarchy. // if (up_) { bool down = down_; down_ = false; for (Type::BegetsIterator i (c.begets_begin ()); i != c.begets_end (); ++i) { dispatch (i->derived ()); } down_ = down; } } private: bool up_, down_; }; virtual void traverse (SemanticGraph::Any& a) { using SemanticGraph::Compositor; // Find our complex type. // Compositor* c (&a.contained_particle ().compositor ()); while(!c->contained_compositor_p ()) c = &c->contained_particle ().compositor (); SemanticGraph::Complex& type ( dynamic_cast ( c->contained_compositor ().container ())); Complex complex; Traversal::Names names; Element element (*this, a); complex >> names >> element; complex.dispatch (type); } }; // // struct Traverser: Traversal::Schema, Traversal::Complex, Traversal::Type, Traversal::Element, ValidationContext { Traverser (ValidationContext& c) : ValidationContext (c), any_ (c) { *this >> sources_ >> *this; *this >> schema_names_ >> ns_ >> names_; names_ >> *this >> names_; // Any // if (!is_disabled ("T001")) { *this >> contains_compositor_ >> compositor_ >> contains_particle_; contains_particle_ >> compositor_; contains_particle_ >> any_; } } virtual void traverse (SemanticGraph::Complex& c) { using SemanticGraph::Schema; traverse (static_cast (c)); if (c.inherits_p ()) { SemanticGraph::Type& t (c.inherits ().base ()); if (t.named_p () && types_.find ( t.scope ().name () + L"#" + t.name ()) == types_.end ()) { // Don't worry about types that are in included/imported // schemas. // Schema& s (dynamic_cast (t.scope ().scope ())); if (&s == &schema_root || sources_p (schema_root, s)) { valid = false; os << c.file () << ":" << c.line () << ":" << c.column () << ": error: type '" << xpath (c) << "' inherits from " << "yet undefined type '" << xpath (t) << "'" << endl; os << t.file () << ":" << t.line () << ":" << t.column () << ": info: '" << xpath (t) << "' is defined here" << endl; os << c.file () << ":" << c.line () << ":" << c.column () << ": info: inheritance from a yet-undefined type is " << "not supported" << endl; os << c.file () << ":" << c.line () << ":" << c.column () << ": info: re-arrange your schema and try again" << endl; } } } Complex::traverse (c); } virtual void traverse (SemanticGraph::Type& t) { // This is also used to traverse Complex. // if (t.named_p ()) { types_.insert (t.scope ().name () + L"#" + t.name ()); } } virtual void traverse (SemanticGraph::Element& e) { if (is_disabled ("T002")) return; // Note that there is no test for generate_p since we want // to catch cases when things are not being generated but // most likely should have been. // if (e.substitutes_p () && !polymorphic && !subst_group_warning_issued) { subst_group_warning_issued = true; os << e.file () << ":" << e.line () << ":" << e.column () << ": warning T002: substitution groups are used but " << "--generate-polymorphic was not specified" << endl; os << e.file () << ":" << e.line () << ":" << e.column () << ": info: generated code may not be able to handle " << "some conforming instances" << endl; } } // Return true if root sources s. // bool sources_p (SemanticGraph::Schema& root, SemanticGraph::Schema& s) { using SemanticGraph::Schema; using SemanticGraph::Sources; for (Schema::UsesIterator i (root.uses_begin ()); i != root.uses_end (); ++i) { if (i->is_a ()) { if (&i->schema () == &s || sources_p (i->schema (), s)) return true; } } return false; } private: set types_; Sources sources_; Traversal::Names schema_names_; Traversal::Namespace ns_; Traversal::Names names_; // Any. // Any any_; Traversal::Compositor compositor_; Traversal::ContainsParticle contains_particle_; Traversal::ContainsCompositor contains_compositor_; }; struct AnonymousType: Traversal::Schema, Traversal::Complex, Traversal::Element, Traversal::Attribute, ValidationContext { AnonymousType (ValidationContext& c) : ValidationContext (c), anonymous_error_issued_ (false) { *this >> sources_ >> *this; *this >> schema_names_ >> ns_ >> names_ >> *this; *this >> names_; } bool traverse_common (SemanticGraph::Member& m) { SemanticGraph::Type& t (m.type ()); if (!t.named_p () && !t.is_a () && !t.is_a ()) { if (!anonymous_error_issued_) { valid = false; anonymous_error_issued_ = true; wcerr << t.file () << ": error: anonymous types detected" << endl; wcerr << t.file () << ": info: " << "anonymous types are not supported in this mapping" << endl; wcerr << t.file () << ": info: consider explicitly naming these types or " << "remove the --preserve-anonymous option to " << "automatically name them" << endl; if (!options.show_anonymous ()) wcerr << t.file () << ": info: use --show-anonymous option to see these " << "types" << endl; } return true; } return false; } virtual void traverse (SemanticGraph::Element& e) { if (skip (e)) return; if (traverse_common (e)) { if (options.show_anonymous ()) { wcerr << e.file () << ":" << e.line () << ":" << e.column () << ": error: element '" << xpath (e) << "' " << "is of anonymous type" << endl; } } else Traversal::Element::traverse (e); } virtual void traverse (SemanticGraph::Attribute& a) { if (traverse_common (a)) { if (options.show_anonymous ()) { wcerr << a.file () << ":" << a.line () << ":" << a.column () << ": error: attribute '" << xpath (a) << "' " << "is of anonymous type" << endl; } } else Traversal::Attribute::traverse (a); } private: bool anonymous_error_issued_; set types_; Sources sources_; Traversal::Names schema_names_; Traversal::Namespace ns_; Traversal::Names names_; }; } bool Validator:: validate (options const& ops, SemanticGraph::Schema& schema, SemanticGraph::Path const& path, const WarningSet& disabled_warnings, Counts const& counts) { bool valid (true); ValidationContext ctx ( schema, path, ops, disabled_warnings, counts, false, valid); // // bool import_maps (ops.import_maps ()); bool export_maps (ops.export_maps ()); if (import_maps && export_maps) { wcerr << "error: --import-maps and --export-maps are " << "mutually exclusive" << endl; return false; } if (import_maps && !ctx.polymorphic) { wcerr << "error: --import-maps can only be specified together with " << "--generate-polymorphic" << endl; return false; } if (export_maps && !ctx.polymorphic) { wcerr << "error: --export-maps can only be specified together " << "with --generate-polymorphic" << endl; return false; } // // if (ops.char_type () != "char" && ops.char_type () != "wchar_t" && !ctx.is_disabled ("T003")) { wcerr << "warning T003: unknown base character type '" << ops.char_type ().c_str () << "'" << endl; } // // NarrowString tn (ops.type_naming ()); if (tn != "knr" && tn != "ucc" && tn != "java") { wcerr << "error: unknown type naming style specified: '" << tn.c_str () << "'" << endl; return false; } NarrowString fn (ops.function_naming ()); if (fn != "knr" && fn != "lcc" && fn != "java") { wcerr << "error: unknown function naming style specified: '" << fn.c_str () << "'" << endl; return false; } // // bool element_type (ops.generate_element_type ()); bool par (!ops.suppress_parsing ()); bool ser (ops.generate_serialization ()); if (ops.generate_element_map ()) { if (!element_type) { wcerr << "error: --generate-element-map can only be specified " << "together with --generate-element-type" << endl; return false; } if (!(par || ser)) { wcerr << "error: --generate-element-map is specified but " << "neither parsing nor serialization code is generated" << endl; return false; } } // Issue a warning if there are more than one global element // and we are generating parsing/serialization functions or // element types for all of them by default. // if (counts.global_elements > 1 && (element_type || par || ser) && !ops.root_element_first () && !ops.root_element_last () && !ops.root_element_all () && !ops.root_element_none () && ops.root_element ().empty () && !ctx.is_disabled ("T004")) { wcerr << schema.file () << ": warning T004: generating "; if (element_type) wcerr << "element types"; else { wcerr << (par ? "parsing " : "") << (ser ? (par ? "and serialization " : "serialization ") : "") << "functions"; } wcerr << " for " << counts.global_elements << " global elements" << endl; wcerr << schema.file () << ": info: use --root-element-* options " << "to specify document root(s)" << endl; } // Test for anonymout types. // { AnonymousType traverser (ctx); traverser.dispatch (schema); } // Test the rest. // if (valid) { Traverser traverser (ctx); traverser.dispatch (schema); } return valid; // T005 is used in polymorphism-processor.cxx. // } } }