// file : xsd-frontend/transformations/enum-synthesis.cxx // copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC // license : GNU GPL v2 + exceptions; see accompanying LICENSE file #include #include #include #include namespace XSDFrontend { namespace { typedef std::set Enumerators; struct Enumerator: Traversal::Enumerator { Enumerator (SemanticGraph::Schema& s, SemanticGraph::Enumeration& e, Enumerators& enumerators) : schema_ (s), enum_ (e), enumerators_ (enumerators) { } virtual void traverse (Type& e) { String const& name (e.name ()); if (enumerators_.find (name) == enumerators_.end ()) { enumerators_.insert (name); // Clone the enumerator and add it to enum_. // Type& c (schema_.new_node (e.file (), e.line (), e.column ())); schema_.new_edge (enum_, c, name); schema_.new_edge (c, enum_); if (e.annotated_p ()) schema_.new_edge (e.annotation (), c); } } private: SemanticGraph::Schema& schema_; SemanticGraph::Enumeration& enum_; Enumerators& enumerators_; }; // // struct Union: Traversal::Union { Union (SemanticGraph::Schema& schema) : schema_ (schema) { } virtual void traverse (Type& u) { using SemanticGraph::Enumeration; SemanticGraph::Context& uc (u.context ()); if (uc.count ("xsd-frontend-enum-synthesis-processed")) return; uc.set ("xsd-frontend-enum-synthesis-processed", true); // First see if this union is suitable for synthesis. // SemanticGraph::Type* base (0); for (Type::ArgumentedIterator i (u.argumented_begin ()); i != u.argumented_end (); ++i) { if (i->type ().is_a ()) { // See if we can synthesize an enum for this union. This // call can change the value i->type() returns. // dispatch (i->type ()); } SemanticGraph::Type& t (i->type ()); if (!t.is_a ()) return; // Make sure all the enums have a common base. // if (base == 0) base = &t; else { // Traverse the inheritance hierarchy until we fine a // common base. // while (base != 0) { SemanticGraph::Type* b (&t); for (; b != base && b->inherits_p (); b = &b->inherits ().base ()) ; if (base == b) break; // Could not find any match on this level. Go one step // lower and try again. // base = base->inherits_p () ? &base->inherits ().base () : 0; } if (base == 0) return; // No common base. } } if (base == 0) return; // Empty union. // So this union is suitable for synthesis. Base variable points // to the "most-derived" common base type. // Enumeration& e (schema_.new_node ( u.file (), u.line (), u.column ())); schema_.new_edge (e, *base); // Copy enumerators from the member enums. // { Enumerators set; Traversal::Enumeration en; Traversal::Names names; Enumerator er (schema_, e, set); en >> names >> er; for (Type::ArgumentedIterator i (u.argumented_begin ()); i != u.argumented_end (); ++i) { en.dispatch (i->type ()); } } // Reset edges pointing to union to point to enum. // if (u.annotated_p ()) { schema_.reset_right_node (u.annotated (), e); schema_.add_edge_right (e, u.annotated ()); } schema_.reset_right_node (u.named (), e); schema_.add_edge_right (e, u.named ()); for (Type::ClassifiesIterator i (u.classifies_begin ()), end (u.classifies_end ()); i != end; ++i) { schema_.reset_right_node (*i, e); schema_.add_edge_right (e, *i); } for (Type::BegetsIterator i (u.begets_begin ()), end (u.begets_end ()); i != end; ++i) { schema_.reset_right_node (*i, e); schema_.add_edge_right (e, *i); } for (Type::ArgumentsIterator i (u.arguments_begin ()), end (u.arguments_end ()); i != end; ++i) { schema_.reset_left_node (*i, e); schema_.add_edge_left (e, *i); } // Remove Arguments edges pointing to the union. // while (u.argumented_begin () != u.argumented_end ()) { SemanticGraph::Arguments& a (*u.argumented_begin ()); schema_.delete_edge (a.type (), a.specialization (), a); } // Copy context and delete the union node. // e.context ().swap (uc); schema_.delete_node (u); } private: SemanticGraph::Schema& schema_; }; // 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-enum-synthesis-seen")) { s.context ().set ("xsd-frontend-enum-synthesis-seen", true); Traversal::Uses::traverse (u); } } }; } namespace Transformations { void EnumSynthesis:: transform (SemanticGraph::Schema& s, SemanticGraph::Path const&) { Traversal::Schema schema; Uses uses; schema >> uses >> schema; Traversal::Names schema_names; Traversal::Namespace ns; Traversal::Names ns_names; Union u (s); schema >> schema_names >> ns >> ns_names >> u; // Some twisted schemas do recusive inclusions. // s.context ().set ("xsd-frontend-enum-synthesis-seen", true); schema.dispatch (s); } } }