// file : xsd-frontend/transformations/anonymous.cxx // copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC // license : GNU GPL v2 + exceptions; see accompanying LICENSE file #include #include #include #include #include #include using std::wcerr; using std::endl; namespace XSDFrontend { namespace { using Transformations::AnonymousNameTranslator; // // struct CompareMembers: Traversal::Element, Traversal::Attribute, Traversal::Any, Traversal::AnyAttribute { CompareMembers (SemanticGraph::Nameable& m, bool& r) : member_ (m), result_ (r) { } virtual void traverse (SemanticGraph::Element& x) { using SemanticGraph::Element; Element& y (dynamic_cast (member_)); // Check cardinalities. // if (x.min () != y.min () || x.max () != y.max ()) return; traverse_member (x); } virtual void traverse (SemanticGraph::Attribute& x) { using SemanticGraph::Attribute; Attribute& y (dynamic_cast (member_)); // Check cardinalities. // if (x.optional_p () != y.optional_p ()) return; traverse_member (x); } virtual void traverse_member (SemanticGraph::Member& x) { using SemanticGraph::Member; Member& y (dynamic_cast (member_)); // Check name. // if (x.name () != y.name ()) return; // Check namespace. // if (x.qualified_p () || y.qualified_p ()) { if (!x.qualified_p () || !y.qualified_p ()) return; if (x.namespace_ ().name () != y.namespace_ ().name ()) return; } // Check type. // // @@ What if types are anonymous and structurally equal? // if (&x.type () != &y.type ()) return; // Check default/fixed values. // if (x.default_p () != y.default_p () || x.fixed_p () != y.fixed_p ()) return; if (x.default_p () && x.value () != y.value ()) return; result_ = true; } virtual void traverse (SemanticGraph::Any&) { //@@ TODO } virtual void traverse (SemanticGraph::AnyAttribute&) { //@@ TODO } private: SemanticGraph::Nameable& member_; bool& result_; }; // Compare two types for structural equality. // struct CompareTypes: Traversal::List, Traversal::Union, Traversal::Enumeration, Traversal::Complex { CompareTypes (SemanticGraph::Type& t, bool& r) : type_ (t), result_ (r) { } virtual void traverse (SemanticGraph::List&) { using SemanticGraph::List; //List& y (dynamic_cast (type_)); } virtual void traverse (SemanticGraph::Union& x) { using SemanticGraph::Union; Union& y (dynamic_cast (type_)); Union::ArgumentedIterator ix (x.argumented_begin ()), iy (y.argumented_begin ()); for (; ix != x.argumented_end () && iy != y.argumented_end (); ++ix, ++iy) { // @@ Anon structurally equivalent. // if (&iy->type () != &ix->type ()) return; } result_ = true; } virtual void traverse (SemanticGraph::Enumeration& x) { using SemanticGraph::Enumeration; Enumeration& y (dynamic_cast (type_)); // Bases should be the same. // if (&x.inherits ().base () != &y.inherits ().base ()) return; // Make sure facets match. // using SemanticGraph::Restricts; Restricts& rx (dynamic_cast (x.inherits ())); Restricts& ry (dynamic_cast (y.inherits ())); if (rx.facets () != ry.facets ()) return; // Compare enumerators. // using SemanticGraph::Scope; Scope::NamesIterator ix (x.names_begin ()), iy (y.names_begin ()); for (; ix != x.names_end () && iy != y.names_end (); ++ix, ++iy) { if (ix->name () != iy->name ()) return; } if (ix != x.names_end () || iy != y.names_end ()) return; result_ = true; } virtual void traverse (SemanticGraph::Complex& x) { using SemanticGraph::Complex; Complex& y (dynamic_cast (type_)); // Check inheritance. // if (x.inherits_p () || y.inherits_p ()) { // They both must inherits. // if (!x.inherits_p () || !y.inherits_p ()) return; // With the same kind of inheritance (restriction or extension). // if (typeid (x.inherits ()) != typeid (y.inherits ())) return; // Bases should be the same. // // @@ What if bases are anonymous? // if (&x.inherits ().base () != &y.inherits ().base ()) return; // If it is a restriction, make sure facets match. // using SemanticGraph::Restricts; if (x.inherits ().is_a ()) { Restricts& rx (dynamic_cast (x.inherits ())); Restricts& ry (dynamic_cast (y.inherits ())); if (rx.facets () != ry.facets ()) return; } } // Check the member list. // // @@ Ignoring compositors at the moment. // using SemanticGraph::Scope; Scope::NamesIterator ix (x.names_begin ()), iy (y.names_begin ()); for (; ix != x.names_end () && iy != y.names_end (); ++ix, ++iy) { if (typeid (ix->named ()) != typeid (iy->named ())) return; bool equal (false); CompareMembers t (iy->named (), equal); t.dispatch (ix->named ()); if (!equal) return; } if (ix != x.names_end () || iy != y.names_end ()) return; result_ = true; } private: SemanticGraph::Type& type_; bool& result_; }; // // class Context { public: Context (SemanticGraph::Schema& schema_, SemanticGraph::Path const& file, AnonymousNameTranslator& trans_, bool du) : schema_path_ (file), ns_ (0), failed_ (false), trans (trans_), detect_unstable (du), schema (schema_), schema_path (schema_path_), ns (ns_), failed (failed_) { } protected: Context (Context& c) : trans (c.trans), detect_unstable (c.detect_unstable), schema (c.schema), schema_path (c.schema_path), ns (c.ns), failed (c.failed) { } public: bool structurally_equal (SemanticGraph::Type& x, SemanticGraph::Type& y) { if (typeid (x) != typeid (y)) return false; bool r (false); CompareTypes t (y, r); t.dispatch (x); return r; } struct UnstableConflict { UnstableConflict (SemanticGraph::Type& type) : type_ (type) { } SemanticGraph::Type& type () const { return type_; } private: SemanticGraph::Type& type_; }; SemanticGraph::Type* conflict (String const& name) { using SemanticGraph::Type; using SemanticGraph::Schema; if (Type* t1 = find (schema, name)) { // Check if this is a stable conflict. A conflict is unstable // if a conflicting type is visible from the root schema but // is not visible from the schema where the conflicting // element is defined. // if (detect_unstable) { Schema& s (dynamic_cast (ns->scope ())); Type* t2 (find (s, name)); if (t1 != t2) throw UnstableConflict (*t1); } return t1; } return 0; } SemanticGraph::Type* find (SemanticGraph::Schema& schema, String const& name) { using SemanticGraph::Type; using SemanticGraph::Scope; using SemanticGraph::Namespace; String ns_name (ns->name ()); // Get all namespaces across include/import hierarchy with // our namespace name. // Scope::NamesIteratorPair nip (schema.find (ns_name)); for (; nip.first != nip.second; ++nip.first) { Namespace& ns (dynamic_cast (nip.first->named ())); Scope::NamesIteratorPair types (ns.find (name)); for (; types.first != types.second; ++types.first) { if (Type* t = dynamic_cast (&types.first->named ())) { return t; } } } return 0; } public: SemanticGraph::Path path (SemanticGraph::Nameable& n) { using SemanticGraph::Scope; using SemanticGraph::Schema; using SemanticGraph::Uses; Schema* schema (0); for (Scope* s (dynamic_cast (&n) ? dynamic_cast (&n) : &n.scope ());; s = &s->scope ()) { if ((schema = dynamic_cast (s))) break; } if (!schema->used_p ()) return schema_path; Uses& u (*schema->used_begin ()); return u.path (); } public: String xpath (SemanticGraph::Nameable& n) { if (dynamic_cast (&n) != 0) return L""; // There is a bug if you see this. assert (n.named_p ()); SemanticGraph::Scope& scope (n.scope ()); if (dynamic_cast (&scope) != 0) return n.name (); return xpath (scope) + L"/" + n.name (); } private: SemanticGraph::Path const schema_path_; SemanticGraph::Namespace* ns_; bool failed_; public: AnonymousNameTranslator& trans; bool detect_unstable; public: SemanticGraph::Schema& schema; SemanticGraph::Path const& schema_path; SemanticGraph::Namespace*& ns; bool& failed; }; // Go into implied/included/imported schemas while making sure // we don't process the same stuff more than once. // struct Uses: Traversal::Uses { virtual void traverse (Type& u) { SemanticGraph::Schema& s (u.schema ()); if (!s.context ().count ("xsd-frontend-anonymous-seen")) { s.context ().set ("xsd-frontend-anonymous-seen", true); Traversal::Uses::traverse (u); } } }; // Keep track which namespace we are in. // struct Namespace: Traversal::Namespace { Namespace (SemanticGraph::Namespace*& ns) : ns_ (ns) { } void pre (SemanticGraph::Namespace& ns) { ns_ = &ns; } void post (SemanticGraph::Namespace&) { ns_ = 0; } private: SemanticGraph::Namespace*& ns_; }; // // struct Type: Traversal::List, Traversal::Union, Traversal::Complex, protected virtual Context { Type (Context& c) : Context (c) { } virtual void traverse (SemanticGraph::List& l) { SemanticGraph::Type& t (l.argumented ().type ()); //@@ This IDREF stuff is really ugly! // if (!t.named_p () && !t.is_a () && !t.is_a ()) { try { // Run the name through the translation service. // SemanticGraph::Path file (path (l)); file.normalize (); String file_str; // Try to use the portable representation of the path. If that // fails, fall back to the native representation. // try { file_str = file.posix_string (); } catch (SemanticGraph::InvalidPath const&) { file_str = file.string (); } String name ( trans.translate ( file_str, ns->name (), l.name () + L"_item", xpath (l))); // Make sure the name is unique. // unsigned long n (1); String escaped (name); while (conflict (escaped)) { std::wostringstream os; os << n++; escaped = name + os.str (); } t.context ().set ("anonymous", true); schema.new_edge (*ns, t, escaped); } catch (UnstableConflict const& ex) { SemanticGraph::Type& t (ex.type ()); wcerr << l.file () << ":" << l.line () << ":" << l.column () << ": error: list type name '" << xpath (l) << "' " << "creates an unstable conflict when used as a base " << "for the item type name" << endl; wcerr << t.file () << ":" << t.line () << ":" << t.column () << ": info: conflicting type is defined here" << endl; wcerr << l.file () << ":" << l.line () << ":" << l.column () << ": info: " << "use --anonymous-regex to resolve this conflict" << endl; wcerr << l.file () << ":" << l.line () << ":" << l.column () << ": info: " << "and don't forget to pass the same option when " << "translating '" << l.file ().leaf () << "' and all " << "the schemas that refer to it" << endl; failed = true; } } } virtual void traverse (SemanticGraph::Union& u) { String file_str; for (SemanticGraph::Union::ArgumentedIterator i ( u.argumented_begin ()); i != u.argumented_end (); ++i) { SemanticGraph::Type& t (i->type ()); if (!t.named_p () && !t.is_a () && !t.is_a ()) { try { // Run the name through the translation service. // if (!file_str) { SemanticGraph::Path file (path (u)); file.normalize (); // Try to use the portable representation of the path. If // that fails, fall back to the native representation. // try { file_str = file.posix_string (); } catch (SemanticGraph::InvalidPath const&) { file_str = file.string (); } } String name ( trans.translate ( file_str, ns->name (), u.name () + L"_member", xpath (u))); // Make sure the name is unique. // unsigned long n (1); String escaped (name); while (conflict (escaped)) { std::wostringstream os; os << n++; escaped = name + os.str (); } t.context ().set ("anonymous", true); schema.new_edge (*ns, t, escaped); } catch (UnstableConflict const& ex) { SemanticGraph::Type& t (ex.type ()); wcerr << u.file () << ":" << u.line () << ":" << u.column () << ": error: union type name '" << xpath (u) << "' " << "creates an unstable conflict when used as a base " << "for the member type name" << endl; wcerr << t.file () << ":" << t.line () << ":" << t.column () << ": info: conflicting type is defined here" << endl; wcerr << u.file () << ":" << u.line () << ":" << u.column () << ": info: " << "use --anonymous-regex to resolve this conflict" << endl; wcerr << u.file () << ":" << u.line () << ":" << u.column () << ": info: " << "and don't forget to pass the same option when " << "translating '" << u.file ().leaf () << "' and all " << "the schemas that refer to it" << endl; failed = true; } } } } virtual void traverse (SemanticGraph::Complex& c) { if (!c.inherits_p ()) return; SemanticGraph::Type& t (c.inherits ().base ()); //@@ This IDREF stuff is really ugly! // if (!t.named_p () && !t.is_a () && !t.is_a ()) { try { // Run the name through the translation service. // SemanticGraph::Path file (path (c)); file.normalize (); String file_str; // Try to use the portable representation of the path. If that // fails, fall back to the native representation. // try { file_str = file.posix_string (); } catch (SemanticGraph::InvalidPath const&) { file_str = file.string (); } String name ( trans.translate ( file_str, ns->name (), c.name () + L"_base", xpath (c))); // Make sure the name is unique. // unsigned long n (1); String escaped (name); while (conflict (escaped)) { std::wostringstream os; os << n++; escaped = name + os.str (); } t.context ().set ("anonymous", true); schema.new_edge (*ns, t, escaped); } catch (UnstableConflict const& ex) { SemanticGraph::Type& t (ex.type ()); wcerr << c.file () << ":" << c.line () << ":" << c.column () << ": error: simple type name '" << xpath (c) << "' " << "creates an unstable conflict when used as a base " << "for the base type name" << endl; wcerr << t.file () << ":" << t.line () << ":" << t.column () << ": info: conflicting type is defined here" << endl; wcerr << c.file () << ":" << c.line () << ":" << c.column () << ": info: " << "use --anonymous-regex to resolve this conflict" << endl; wcerr << c.file () << ":" << c.line () << ":" << c.column () << ": info: " << "and don't forget to pass the same option when " << "translating '" << c.file ().leaf () << "' and all " << "the schemas that refer to it" << endl; failed = true; } } } }; // // struct Member: Traversal::Element, Traversal::Attribute, protected virtual Context { Member (Context& c) : Context (c) { } virtual void traverse (SemanticGraph::Element& e) { SemanticGraph::Type& t (e.type ()); //@@ This IDREF stuff is really ugly! // if (!t.named_p () && !t.is_a () && !t.is_a ()) { try { traverse_ (e); } catch (UnstableConflict const& ex) { SemanticGraph::Type& t (ex.type ()); wcerr << e.file () << ":" << e.line () << ":" << e.column () << ": error: element name '" << xpath (e) << "' " << "creates an unstable conflict when used as a type name" << endl; wcerr << t.file () << ":" << t.line () << ":" << t.column () << ": info: conflicting type is defined here" << endl; wcerr << e.file () << ":" << e.line () << ":" << e.column () << ": info: " << "use --anonymous-regex to resolve this conflict" << endl; wcerr << e.file () << ":" << e.line () << ":" << e.column () << ": info: " << "and don't forget to pass the same option when " << "translating '" << e.file ().leaf () << "' and all " << "the schemas that refer to it" << endl; failed = true; } } } virtual void traverse (SemanticGraph::Attribute& a) { SemanticGraph::Type& t (a.type ()); //@@ This IDREF stuff us really ugly! // if (!t.named_p () && !t.is_a () && !t.is_a ()) { try { traverse_ (a); } catch (UnstableConflict const& ex) { SemanticGraph::Type& t (ex.type ()); wcerr << a.file () << ":" << a.line () << ":" << a.column () << ": error: attribute name '" << xpath (a) << "' " << "creates an unstable conflict when used as a type name" << endl; wcerr << t.file () << ":" << t.line () << ":" << t.column () << ": info: conflicting type is defined here" << endl; wcerr << a.file () << ":" << a.line () << ":" << a.column () << ": info: " << "use --anonymous-regex to resolve this conflict" << endl; wcerr << a.file () << ":" << a.line () << ":" << a.column () << ": info: " << "and don't forget to pass the same option when " << "translating '" << a.file ().leaf () << "' and all " << "the schemas that refer to it" << endl; failed = true; } } } void traverse_ (SemanticGraph::Member& m) { using SemanticGraph::Type; Type& t (m.type ()); // Normally this will be the member which also "defined" the type. // However, in some cases of cyclic schema inclusion, this does // not happen. As a result we need an extra check that will make // sure we create the Names edge in the same Schema node as the // one which contains the member which initially defined this // type. See the cyclic-inclusion test for an example. // // Find the first member that this type classifies. // for (Type::ClassifiesIterator i (t.classifies_begin ()); i != t.classifies_end (); ++i) { SemanticGraph::Instance& inst (i->instance ()); if (inst.is_a ()) { // If it is the same member as the one we are traversing, // then continue. // if (&inst == &m) break; else return; } } // Run the name through the translation service. // SemanticGraph::Path file (path (m)); file.normalize (); String file_str; // Try to use the portable representation of the path. If that // fails, fall back to the native representation. // try { file_str = file.posix_string (); } catch (SemanticGraph::InvalidPath const&) { file_str = file.string (); } String name ( trans.translate (file_str, ns->name (), m.name (), xpath (m))); // Check if this name conflicts. // unsigned long n (1); String escaped (name); while (SemanticGraph::Type* other = conflict (escaped)) { // First see if we should just use the other type. It should // also have been anonymous and structurally equal to our type. // if (other->context ().count ("anonymous")) { if (structurally_equal (t, *other)) { // Reset the elements that are classified by this type to point // to the other type. // for (Type::ClassifiesIterator i (t.classifies_begin ()); i != t.classifies_end (); ++i) { schema.reset_right_node (*i, *other); } //wcerr << "equal " << name << endl; return; } //else //wcerr << "unequal " << name << endl; } std::wostringstream os; os << n++; escaped = name + os.str (); } t.context ().set ("anonymous", true); schema.new_edge (*ns, t, escaped); } }; } namespace Transformations { Anonymous:: Anonymous (AnonymousNameTranslator& trans) : trans_ (trans) { } void Anonymous:: transform (SemanticGraph::Schema& s, SemanticGraph::Path const& f, bool duc) { Context ctx (s, f, trans_, duc); Traversal::Schema schema; Uses uses; schema >> uses >> schema; Traversal::Names schema_names; Namespace ns (ctx.ns); Traversal::Names ns_names_member; Traversal::Names ns_names_type; schema >> schema_names >> ns; ns >> ns_names_member; ns >> ns_names_type; Type type (ctx); ns_names_type >> type; Traversal::Scope scope; // Goes to both types and groups. Member member (ctx); ns_names_member >> scope; ns_names_member >> member; Traversal::Names names; scope >> names >> member; // Some twisted schemas do recusive inclusions. // s.context ().set ("xsd-frontend-anonymous-seen", true); schema.dispatch (s); if (ctx.failed) throw Failed (); } AnonymousNameTranslator:: ~AnonymousNameTranslator () { } } }