aboutsummaryrefslogtreecommitdiff
path: root/xsd-frontend/transformations/enum-synthesis.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'xsd-frontend/transformations/enum-synthesis.cxx')
-rw-r--r--xsd-frontend/transformations/enum-synthesis.cxx249
1 files changed, 249 insertions, 0 deletions
diff --git a/xsd-frontend/transformations/enum-synthesis.cxx b/xsd-frontend/transformations/enum-synthesis.cxx
new file mode 100644
index 0000000..4a5156f
--- /dev/null
+++ b/xsd-frontend/transformations/enum-synthesis.cxx
@@ -0,0 +1,249 @@
+// file : xsd-frontend/transformations/enum-synthesis.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2006-2010 Code Synthesis Tools CC
+// license : GNU GPL v2 + exceptions; see accompanying LICENSE file
+
+#include <xsd-frontend/transformations/enum-synthesis.hxx>
+
+#include <xsd-frontend/semantic-graph.hxx>
+#include <xsd-frontend/traversal.hxx>
+
+#include <cult/containers/set.hxx>
+
+namespace XSDFrontend
+{
+ using namespace Cult;
+ typedef WideString String;
+
+ namespace
+ {
+ typedef Cult::Containers::Set<String> 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<Type> (e.file (), e.line (), e.column ()));
+
+ schema_.new_edge<SemanticGraph::Names> (enum_, c, name);
+ schema_.new_edge<SemanticGraph::Belongs> (c, enum_);
+
+ if (e.annotated_p ())
+ schema_.new_edge<SemanticGraph::Annotates> (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<SemanticGraph::Union> ())
+ {
+ // 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<Enumeration> ())
+ 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<Enumeration> (
+ u.file (), u.line (), u.column ()));
+
+ schema_.new_edge<SemanticGraph::Restricts> (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);
+ }
+ }
+}