diff options
author | Jörg Frings-Fürst <debian@jff.email> | 2025-03-19 15:41:36 +0100 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff.email> | 2025-03-19 15:41:36 +0100 |
commit | 018e1ba581ec6f01f069a45ec4cf89f152b44d5f (patch) | |
tree | 0e7dda4bb693a6714066fbe5efcd2f24ff7c1a65 /libxsd-frontend/xsd-frontend/transformations/restriction.cxx | |
parent | 1c188393cd2e271ed2581471b601fb5960777fd8 (diff) |
remerge
Diffstat (limited to 'libxsd-frontend/xsd-frontend/transformations/restriction.cxx')
-rw-r--r-- | libxsd-frontend/xsd-frontend/transformations/restriction.cxx | 576 |
1 files changed, 576 insertions, 0 deletions
diff --git a/libxsd-frontend/xsd-frontend/transformations/restriction.cxx b/libxsd-frontend/xsd-frontend/transformations/restriction.cxx new file mode 100644 index 0000000..edd74be --- /dev/null +++ b/libxsd-frontend/xsd-frontend/transformations/restriction.cxx @@ -0,0 +1,576 @@ +// file : xsd-frontend/transformations/restriction.cxx +// copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#include <vector> +#include <iostream> + +#include <xsd-frontend/semantic-graph.hxx> +#include <xsd-frontend/traversal.hxx> + +#include <xsd-frontend/transformations/restriction.hxx> + +using namespace std; + +namespace XSDFrontend +{ + typedef Transformations::Restriction::Failed Failed; + typedef std::vector<SemanticGraph::Complex*> BaseList; + + namespace + { + // + // + struct Complex: Traversal::Complex + { + Complex (SemanticGraph::Schema& schema) + : schema_ (schema) + { + } + + virtual void + traverse (Type& c) + { + using namespace SemanticGraph; + using SemanticGraph::Complex; + + if (c.context ().count ("xsd-frontend-restriction-seen")) + return; + + c.context ().set ("xsd-frontend-restriction-seen", true); + + // The base content model can be spread over several types + // in the inheritance-by-extension hierarchy. + // + BaseList base_model; + + // Since attribute wildcards don't have names, we will have + // to rely on their relative position to find association. + // + BaseList base_list; + + // Current implementation of semantic graph uses the same Restricts + // edge for both simple type/content restriction and complex content + // restriction. Here we are interested in the complex content only. + // + // + if (c.inherits_p () && + c.inherits ().is_a<Restricts> () && + !c.inherits ().base ().is_a<AnyType> ()) + { + // Go down our inheritance hierarchy until the end or the previous + // restriction. + // + Complex* base (&c); + + while ((base = dynamic_cast<Complex*> (&base->inherits ().base ()))) + { + traverse (*base); // Make sure our base is processed. + + // Handle attributes. + // + merge_attributes (c, *base); + + base_list.push_back (base); + + // Collect types that have complex content. + // + if (base->contains_compositor_p ()) + base_model.push_back (base); + + if (!base->inherits_p () || base->inherits ().is_a<Restricts> ()) + break; + } + + // Handle attribute wildcards. + // + handle_any_attributes (c, base_list); + + // Handle complex content (not for the faint of heart). + // + if (c.contains_compositor_p ()) + { + // Traverse both restricted content model and base content + // model (in base_model) while looking for matches. + // + Compositor& root (c.contains_compositor ().compositor ()); + + if (base_model.size () == 1) + handle (root, + base_model[0]->contains_compositor ().compositor ()); + else + { + Compositor::ContainsIterator i (root.contains_begin ()); + BaseList::reverse_iterator j (base_model.rbegin ()); + + for (; i != root.contains_end (); ++i, ++j) + { + Particle& p (i->particle ()); + + if (!p.is_a<Compositor> ()) + { + wcerr << p.file () << ":" << p.line () << ":" << p.column () + << ": error: expected compositor instead of particle" + << endl; + throw Failed (); + } + + for (; j != base_model.rend (); ++j) + { + if (match (p, (*j)->contains_compositor ().compositor ())) + { + handle (p, (*j)->contains_compositor ().compositor ()); + break; + } + } + + if (j == base_model.rend ()) + break; + } + + if (i != root.contains_end ()) + { + Particle& p (i->particle ()); + + wcerr << p.file () << ":" << p.line () << ":" << p.column () + << ": error: unable to match restricted compositor" + << endl; + throw Failed (); + } + } + } + } + + // Traverse anonymous types (via elements & attributes). + // + Traversal::Complex::names (c); + } + + private: + void + handle (SemanticGraph::Particle& r, SemanticGraph::Particle& b) + { + using namespace SemanticGraph; + + if (r.is_a<Compositor> ()) + { + Compositor& rc (dynamic_cast<Compositor&> (r)); + Compositor& bc (dynamic_cast<Compositor&> (b)); + + Compositor::ContainsIterator i (rc.contains_begin ()); + Compositor::ContainsIterator j (bc.contains_begin ()); + + for (; i != rc.contains_end (); ++i, ++j) + { + for (; j != bc.contains_end (); ++j) + { + Particle& rp (i->particle ()); + Particle& bp (j->particle ()); + + if (typeid (rp) != typeid (bp)) + continue; + + if (match (rp, bp)) + { + handle (rp, bp); + break; + } + } + + if (j == bc.contains_end ()) + break; + } + + if (i != rc.contains_end ()) + { + Particle& p (i->particle ()); + + wcerr << p.file () << ":" << p.line () << ":" << p.column () + << ": error: unable to match restricted particle" + << endl; + throw Failed (); + } + + rc.context ().set ("xsd-frontend-restriction-correspondence", &bc); + } + else if (r.is_a<Element> ()) + { + // Element + // + r.context ().set ("xsd-frontend-restriction-correspondence", + dynamic_cast<Element*> (&b)); + } + else + { + // Wildcard + // + r.context ().set ("xsd-frontend-restriction-correspondence", + dynamic_cast<Any*> (&b)); + } + } + + bool + match (SemanticGraph::Particle& r, SemanticGraph::Particle& b) + { + using namespace SemanticGraph; + + if (typeid (r) != typeid (b)) + return false; + + if (r.is_a<Compositor> ()) + { + Compositor& rc (dynamic_cast<Compositor&> (r)); + Compositor& bc (dynamic_cast<Compositor&> (b)); + + Compositor::ContainsIterator i (rc.contains_begin ()); + + if (i == rc.contains_end ()) + return true; + + Particle& rp (i->particle ()); + + for (Compositor::ContainsIterator j (bc.contains_begin ()); + j != bc.contains_end (); ++j) + { + Particle& bp (j->particle ()); + + if (typeid (rp) != typeid (bp)) + continue; + + if (match (rp, bp)) + return true; + } + } + else if (r.is_a<Element> ()) + { + Element& re (dynamic_cast<Element&> (r)); + Element& be (dynamic_cast<Element&> (b)); + + if (re.qualified_p ()) + { + if (be.qualified_p () && + re.name () == be.name () && + re.namespace_ ().name () == be.namespace_ ().name ()) + return true; + } + else + { + if (!be.qualified_p () && re.name () == be.name ()) + return true; + } + + // @@ Need to take into account substitution groups. + // + } + else + { + // Wildcard. + // + + // @@ To handle this properly we will need to analyze + // namespaces. + // + return true; + } + + return false; + } + + void + merge_attributes (SemanticGraph::Complex& c, + SemanticGraph::Complex& base) + { + using namespace SemanticGraph; + + for (Scope::NamesIterator i (base.names_begin ()), + e (base.names_end ()); i != e; ++i) + { + Attribute* prot (dynamic_cast<Attribute*> (&i->named ())); + + if (prot == 0) + continue; + + Name name (prot->name ()); + Scope::NamesIteratorPair r (c.find (name)); + + Attribute* a (0); + + for (; r.first != r.second; ++r.first) + { + a = dynamic_cast<Attribute*> (&r.first->named ()); + + if (a == 0) + continue; + + if (prot->qualified_p ()) + { + if (a->qualified_p () && + prot->namespace_ ().name () == a->namespace_ ().name ()) + { + break; + } + } + else + { + if (!a->qualified_p ()) + break; + } + + a = 0; + } + + if (a == 0) + { + a = &schema_.new_node<Attribute> (prot->file (), + prot->line (), + prot->column (), + prot->optional_p (), + prot->global_p (), + prot->qualified_p ()); + + schema_.new_edge<Names> (c, *a, name); + + // Transfer namespace. + // + if (prot->qualified_p ()) + { + schema_.new_edge<BelongsToNamespace> (*a, prot->namespace_ ()); + } + + // Default and fixed values if any. + // + if (prot->fixed_p ()) + a->fixed (prot->value ()); + else if (prot->default_p ()) + a->default_ (prot->value ()); + + // Belongs edge. + // + schema_.new_edge<Belongs> (*a, prot->type ()); + + // Transfer annotation. + // + if (prot->annotated_p ()) + schema_.new_edge<Annotates> (prot->annotation (), *a); + } + + a->context ().set ("xsd-frontend-restriction-correspondence", prot); + } + } + + void + handle_any_attributes (SemanticGraph::Complex& c, BaseList& bl) + { + using namespace SemanticGraph; + + BaseList::reverse_iterator bi (bl.rbegin ()), be (bl.rend ()); + Scope::NamesIterator si; + + if (bi != be) + si = (*bi)->names_begin (); + + for (Scope::NamesIterator i (c.names_begin ()), + e (c.names_end ()); i != e; ++i) + { + AnyAttribute* a (dynamic_cast<AnyAttribute*> (&i->named ())); + + if (a == 0) + continue; + + AnyAttribute* p (0); + + while (bi != be) + { + for (; si != (*bi)->names_end (); ++si) + { + p = dynamic_cast<AnyAttribute*> (&si->named ()); + + if (p != 0) + { + ++si; + break; + } + } + + if (p != 0) + break; + + // Didn't find anything in this base. Move on to the next. + // + ++bi; + + if (bi != be) + si = (*bi)->names_begin (); + } + + if (p != 0) + { + a->context ().set ("xsd-frontend-restriction-correspondence", p); + } + else + { + wcerr << a->file () << ":" << a->line () << ":" << a->column () + << ": error: unable to find matching wildcard in base type" + << endl; + throw Failed (); + } + } + } + + private: + SemanticGraph::Schema& schema_; + }; + + // + // + struct Anonymous : Traversal::Element, + Traversal::Attribute + { + Anonymous (Traversal::NodeDispatcher& d1) + : complex_ (&d1, 0) + { + *this >> belongs_ >> complex_; + } + + Anonymous (Traversal::NodeDispatcher& d1, + Traversal::NodeDispatcher& d2) + : complex_ (&d1, &d2) + { + *this >> belongs_ >> complex_; + } + + // Hooks. + // + public: + virtual void + member_pre (SemanticGraph::Member&) + { + } + + virtual void + member_post (SemanticGraph::Member&) + { + } + + public: + + virtual void + traverse (SemanticGraph::Element& e) + { + SemanticGraph::Type& t (e.type ()); + + if (!t.named_p () && !t.context ().count ("seen")) + { + t.context ().set ("seen", true); + + member_pre (e); + + Element::belongs (e, belongs_); + + member_post (e); + + t.context ().remove ("seen"); + } + } + + virtual void + traverse (SemanticGraph::Attribute& a) + { + SemanticGraph::Type& t (a.type ()); + + if (!t.named_p () && !t.context ().count ("seen")) + { + t.context ().set ("seen", true); + + member_pre (a); + + Attribute::belongs (a, belongs_); + + member_post (a); + + t.context ().remove ("seen"); + } + } + + private: + struct Complex : Traversal::Complex + { + Complex (Traversal::NodeDispatcher* d1, + Traversal::NodeDispatcher* d2) + : d1_ (d1), d2_ (d2) + { + } + + virtual void + traverse (SemanticGraph::Complex& c) + { + if (d1_) + d1_->dispatch (c); + + if (d2_) + d2_->dispatch (c); + } + + private: + Traversal::NodeDispatcher* d1_; + Traversal::NodeDispatcher* d2_; + + } complex_; + + Traversal::Belongs belongs_; + }; + + + // 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-restriction-seen")) + { + s.context ().set ("xsd-frontend-restriction-seen", true); + Traversal::Uses::traverse (u); + } + } + }; + } + + namespace Transformations + { + void Restriction:: + 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; + + schema >> schema_names >> ns >> ns_names; + + Complex complex_type (s); + Anonymous anonymous (complex_type); + + ns_names >> complex_type; + ns_names >> anonymous; + + Traversal::Names names; + + complex_type >> names >> anonymous; + + // Some twisted schemas do recusive inclusions. + // + s.context ().set ("xsd-frontend-restriction-seen", true); + + schema.dispatch (s); + } + } +} |