diff options
Diffstat (limited to 'libxsd-frontend/xsd-frontend/transformations')
10 files changed, 2683 insertions, 0 deletions
| diff --git a/libxsd-frontend/xsd-frontend/transformations/anonymous.cxx b/libxsd-frontend/xsd-frontend/transformations/anonymous.cxx new file mode 100644 index 0000000..1c42d98 --- /dev/null +++ b/libxsd-frontend/xsd-frontend/transformations/anonymous.cxx @@ -0,0 +1,1016 @@ +// 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 <xsd-frontend/transformations/anonymous.hxx> + +#include <xsd-frontend/semantic-graph.hxx> +#include <xsd-frontend/traversal.hxx> + +#include <iostream> +#include <sstream> +#include <typeinfo> + +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<Element&> (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<Attribute&> (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&> (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<List&> (type_)); +      } + +      virtual void +      traverse (SemanticGraph::Union& x) +      { +        using SemanticGraph::Union; + +        Union& y (dynamic_cast<Union&> (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<Enumeration&> (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<Restricts&> (x.inherits ())); +        Restricts& ry (dynamic_cast<Restricts&> (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<Complex&> (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> ()) +          { +            Restricts& rx (dynamic_cast<Restricts&> (x.inherits ())); +            Restricts& ry (dynamic_cast<Restricts&> (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<Schema&> (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<Namespace&> (nip.first->named ())); + +          Scope::NamesIteratorPair types (ns.find (name)); + +          for (; types.first != types.second; ++types.first) +          { +            if (Type* t = dynamic_cast<Type*> (&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<Scope*> (&n) +                       ? dynamic_cast<Scope*> (&n) : &n.scope ());; +             s = &s->scope ()) +        { +          if ((schema = dynamic_cast<Schema*> (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<SemanticGraph::Namespace*> (&n) != 0) +          return L"<namespace-level>"; // There is a bug if you see this. + +        assert (n.named_p ()); + +        SemanticGraph::Scope& scope (n.scope ()); + +        if (dynamic_cast<SemanticGraph::Namespace*> (&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<SemanticGraph::Fundamental::IdRef> () && +            !t.is_a<SemanticGraph::Fundamental::IdRefs> ()) +        { +          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<SemanticGraph::Names> (*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<SemanticGraph::Fundamental::IdRef> () && +              !t.is_a<SemanticGraph::Fundamental::IdRefs> ()) +          { +            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<SemanticGraph::Names> (*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<SemanticGraph::Fundamental::IdRef> () && +            !t.is_a<SemanticGraph::Fundamental::IdRefs> ()) +        { +          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<SemanticGraph::Names> (*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<SemanticGraph::Fundamental::IdRef> () && +            !t.is_a<SemanticGraph::Fundamental::IdRefs> ()) +        { +          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<SemanticGraph::Fundamental::IdRef> () && +            !t.is_a<SemanticGraph::Fundamental::IdRefs> ()) +        { +          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<SemanticGraph::Member> ()) +          { +            // 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<SemanticGraph::Names> (*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 () +    { +    } +  } +} diff --git a/libxsd-frontend/xsd-frontend/transformations/anonymous.hxx b/libxsd-frontend/xsd-frontend/transformations/anonymous.hxx new file mode 100644 index 0000000..cafd187 --- /dev/null +++ b/libxsd-frontend/xsd-frontend/transformations/anonymous.hxx @@ -0,0 +1,57 @@ +// file      : xsd-frontend/transformations/anonymous.hxx +// copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +// license   : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef XSD_FRONTEND_TRANSFORMATIONS_ANONYMOUS_HXX +#define XSD_FRONTEND_TRANSFORMATIONS_ANONYMOUS_HXX + +#include <xsd-frontend/types.hxx> + +#include <xsd-frontend/semantic-graph/elements.hxx> // Path +#include <xsd-frontend/semantic-graph/schema.hxx> + +namespace XSDFrontend +{ +  namespace Transformations +  { +    class AnonymousNameTranslator +    { +    public: +      virtual +      ~AnonymousNameTranslator (); + +      // The file argument is empty for the currect translation +      // unit. +      // +      virtual String +      translate (String const& file, +                 String const& ns, +                 String const& name, +                 String const& xpath) = 0; +    }; + +    // This transformation morphs anonymous types into named ones +    // with the names derived from the enclosing attributes and +    // elements. If the detect_unstable_conflicts argument is true +    // then the transformation detects and reports unstable conflicts +    // in name assignment. +    // +    class Anonymous +    { +    public: +      struct Failed {}; + +      Anonymous (AnonymousNameTranslator&); + +      void +      transform (SemanticGraph::Schema&, +                 SemanticGraph::Path const&, +                 bool detect_unstable_conflicts); + +    private: +      AnonymousNameTranslator& trans_; +    }; +  } +} + +#endif // XSD_FRONTEND_TRANSFORMATIONS_ANONYMOUS_HXX diff --git a/libxsd-frontend/xsd-frontend/transformations/enum-synthesis.cxx b/libxsd-frontend/xsd-frontend/transformations/enum-synthesis.cxx new file mode 100644 index 0000000..26ad16c --- /dev/null +++ b/libxsd-frontend/xsd-frontend/transformations/enum-synthesis.cxx @@ -0,0 +1,245 @@ +// 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 <set> + +#include <xsd-frontend/semantic-graph.hxx> +#include <xsd-frontend/traversal.hxx> + +#include <xsd-frontend/transformations/enum-synthesis.hxx> + +namespace XSDFrontend +{ +  namespace +  { +    typedef std::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); +    } +  } +} diff --git a/libxsd-frontend/xsd-frontend/transformations/enum-synthesis.hxx b/libxsd-frontend/xsd-frontend/transformations/enum-synthesis.hxx new file mode 100644 index 0000000..9f0f970 --- /dev/null +++ b/libxsd-frontend/xsd-frontend/transformations/enum-synthesis.hxx @@ -0,0 +1,30 @@ +// file      : xsd-frontend/transformations/enum-synthesis.hxx +// copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +// license   : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef XSD_FRONTEND_TRANSFORMATIONS_ENUM_SYNTHESIS_HXX +#define XSD_FRONTEND_TRANSFORMATIONS_ENUM_SYNTHESIS_HXX + +#include <xsd-frontend/types.hxx> + +#include <xsd-frontend/semantic-graph/elements.hxx> // Path +#include <xsd-frontend/semantic-graph/schema.hxx> + +namespace XSDFrontend +{ +  namespace Transformations +  { +    // This transformation replaces unions of one or more enumerations +    // with the same base with an equivalent synthesized enumeration. +    // This transformation assumes that there are no anonymous types. +    // +    class EnumSynthesis +    { +    public: +      void +      transform (SemanticGraph::Schema&, SemanticGraph::Path const&); +    }; +  } +} + +#endif // XSD_FRONTEND_TRANSFORMATIONS_ENUM_SYNTHESIS_HXX 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); +    } +  } +} diff --git a/libxsd-frontend/xsd-frontend/transformations/restriction.hxx b/libxsd-frontend/xsd-frontend/transformations/restriction.hxx new file mode 100644 index 0000000..6d7410c --- /dev/null +++ b/libxsd-frontend/xsd-frontend/transformations/restriction.hxx @@ -0,0 +1,36 @@ +// file      : xsd-frontend/transformations/restriction.hxx +// copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +// license   : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef XSD_FRONTEND_TRANSFORMATIONS_RESTRICTION_HXX +#define XSD_FRONTEND_TRANSFORMATIONS_RESTRICTION_HXX + +#include <xsd-frontend/types.hxx> + +#include <xsd-frontend/semantic-graph/elements.hxx> // Path +#include <xsd-frontend/semantic-graph/schema.hxx> + +namespace XSDFrontend +{ +  namespace Transformations +  { +    // This transformation performs two major tasks. It transfers omitted +    // attribute declarations from the base to derived-by-restriction type +    // and establishes correspondence between particles and compositors by +    // adding the "xsd-frontend-restriction-correspondence" key-value pair +    // in the context that contains a pointer to the corresponding particle +    // or compositor in the base. Note that restriction of anyType is +    // a special case and is not handled by this transformation. +    // +    class Restriction +    { +    public: +      struct Failed {}; + +      void +      transform (SemanticGraph::Schema&, SemanticGraph::Path const&); +    }; +  } +} + +#endif // XSD_FRONTEND_TRANSFORMATIONS_RESTRICTION_HXX diff --git a/libxsd-frontend/xsd-frontend/transformations/schema-per-type.cxx b/libxsd-frontend/xsd-frontend/transformations/schema-per-type.cxx new file mode 100644 index 0000000..2fc14b5 --- /dev/null +++ b/libxsd-frontend/xsd-frontend/transformations/schema-per-type.cxx @@ -0,0 +1,469 @@ +// file      : xsd-frontend/transformations/schema-per-type.cxx +// copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +// license   : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#include <strings.h> // strcasecmp + +#include <map> +#include <set> +#include <vector> + +#include <sstream> +#include <iostream> + +#include <xsd-frontend/semantic-graph.hxx> +#include <xsd-frontend/traversal.hxx> + +#include <xsd-frontend/transformations/schema-per-type.hxx> + +using std::wcerr; +using std::endl; + +namespace XSDFrontend +{ +  typedef Transformations::SchemaPerType::Failed Failed; +  typedef std::vector<SemanticGraph::Schema*> Schemas; +  typedef std::map<SemanticGraph::Type*, SemanticGraph::Schema*> TypeSchemaMap; + +  // Compare file paths case-insensitively. +  // +  struct FileComparator +  { +    bool +    operator() (NarrowString const& x, NarrowString const& y) const +    { +      return strcasecmp (x.c_str (), y.c_str ()) < 0; +    } +  }; + +  typedef std::set<NarrowString, FileComparator> FileSet; + +  namespace +  { +    // Go into included and imported schemas while making sure +    // we don't process the same stuff more than once. +    // +    struct Uses: Traversal::Includes, +                 Traversal::Imports, +                 Traversal::Implies +    { +      Uses (Schemas& schemas, SemanticGraph::Schema*& xsd) +          : schemas_ (schemas), xsd_ (xsd) +      { +        xsd_ = 0; +      } + +      virtual void +      traverse (SemanticGraph::Includes& i) +      { +        SemanticGraph::Schema& s (i.schema ()); + +        if (!s.context ().count ("xsd-frontend-schema-per-type-seen")) +        { +          schemas_.push_back (&s); +          s.context ().set ("xsd-frontend-schema-per-type-seen", true); +          Traversal::Includes::traverse (i); +        } +      } + +      virtual void +      traverse (SemanticGraph::Imports& i) +      { +        SemanticGraph::Schema& s (i.schema ()); + +        if (!s.context ().count ("xsd-frontend-schema-per-type-seen")) +        { +          schemas_.push_back (&s); +          s.context ().set ("xsd-frontend-schema-per-type-seen", true); +          Traversal::Imports::traverse (i); +        } +      } + +      virtual void +      traverse (SemanticGraph::Implies& i) +      { +        if (xsd_ == 0) +          xsd_ = &i.schema (); +      } + +    private: +      Schemas& schemas_; +      SemanticGraph::Schema*& xsd_; +    }; + +    void +    process_schema (SemanticGraph::Schema& s, +                    SemanticGraph::Schema& root, +                    SemanticGraph::Schema& xsd, +                    TypeSchemaMap& tsm, +                    FileSet& file_set, +                    bool fat_type_file, +                    Transformations::SchemaPerTypeTranslator& trans) +    { +      using namespace SemanticGraph; + +      Path xsd_path ("XMLSchema.xsd"); +      Namespace& ns (dynamic_cast<Namespace&> (s.names_begin ()->named ())); + +      // We should be careful with iterator caching since we are going to +      // remove some of the nodes. +      // +      for (Scope::NamesIterator i (ns.names_begin ()); i != ns.names_end ();) +      { +        Nameable& n (i->named ()); + +        if (n.is_a<Type> ()) +        { +          String name (n.name ()); + +          // Remove from the namespace. +          // +          Scope::NamesIterator tmp (i++); +          root.delete_edge (ns, n, *tmp); + +          // Add a new schema node. +          // +          Path path; +          String tn (trans.translate_type (ns.name (), name)); +          String wbase (tn ? tn : name); + +          try +          { +            NarrowString base (wbase.to_narrow ()); + +            // Escape directory separators unless they came from the +            // translator. +            // +            if (!tn) +            { +              for (NarrowString::iterator i (base.begin ()), e (base.end ()); +                   i != e; ++i) +              { +                if (*i == '/' || *i == '\\') +                  *i = '_'; +              } +            } + +            // Make sure it is unique. +            // +            NarrowString file_name (base); + +            for (unsigned long i (1); +                 file_set.find (file_name) != file_set.end (); +                 ++i) +            { +              std::ostringstream os; +              os << i; +              file_name = base + os.str (); +            } + +            file_set.insert (file_name); +            file_name += ".xsd"; + +            try +            { +              path = Path (file_name); +            } +            catch (InvalidPath const&) +            { +              wcerr << "error: '" << file_name.c_str () << "' is not a valid " +                    << "filesystem path" << endl; + +              wcerr << "info: use type to file name translation mechanism " +                    << "to resolve this" << endl; + +              throw Failed (); +            } +          } +          catch (NonRepresentable const&) +          { +            wcerr << "error: '" << wbase << "' cannot be represented as a " +                  << "narrow string" << endl; + +            wcerr << "info: use type to file name translation mechanism " +                  << "to resolve this" << endl; + +            throw Failed (); +          } + +          Type& t (dynamic_cast<Type&> (n)); + +          Schema& ts (root.new_node<Schema> (path, 1, 1)); +          root.new_edge<Implies> (ts, xsd, xsd_path); + +          Namespace& tns (root.new_node<Namespace> (path, 1, 1)); +          root.new_edge<Names> (ts, tns, ns.name ()); +          root.new_edge<Names> (tns, n, name); + +          // If we are generating fat type files, then also move the global +          // elements this type classifies to the new schema. +          // +          if (fat_type_file) +          { +            for (Type::ClassifiesIterator j (t.classifies_begin ()); +                 j != t.classifies_end (); ++j) +            { +              Instance& e (j->instance ()); + +              // We can only move a global element from the same namespace. +              // +              if (e.is_a<Element> () && +                  e.scope ().is_a<Namespace> () && +                  e.scope ().name () == ns.name ()) +              { +                Names& n (e.named ()); +                String name (n.name ()); + +                // Watch out for the iterator validity: the edge we are +                // about to remove can be from the same list we are +                // currently iterating. +                // +                if (i != ns.names_end () && &*i == &n) +                  ++i; + +                root.delete_edge (n.scope (), e, n); +                root.new_edge<Names> (tns, e, name); +              } +            } +          } + +          // Add include to the original schema and enter into the +          // type-schema map. +          // +          root.new_edge<Includes> (s, ts, path); +          tsm[&t] = &ts; +        } +        else +          ++i; +      } +    } + +    struct Type: Traversal::List, +                 Traversal::Complex, +                 Traversal::Member +    { +      Type (SemanticGraph::Schema& schema, +            SemanticGraph::Schema& root, +            char const* by_value_key, +            TypeSchemaMap& tsm) +          : schema_ (schema), +            root_ (root), +            by_value_key_ (by_value_key), +            tsm_ (tsm) +      { +        *this >> names_ >> *this; +      } + +      virtual void +      traverse (SemanticGraph::List& l) +      { +        // Treat item type as base type since it is impossible +        // to create recursive constructs using list. +        // +        SemanticGraph::Type& t (l.argumented ().type ()); +        set_dep (t, false); +      } + +      virtual void +      traverse (SemanticGraph::Complex& c) +      { +        if (c.inherits_p ()) +          set_dep (c.inherits ().base (), false); + +        Traversal::Complex::names (c); +      } + +      virtual void +      traverse (SemanticGraph::Member& m) +      { +        SemanticGraph::Type& t (m.type ()); + +        bool weak ( +          by_value_key_ == 0 || +          !t.context ().count (by_value_key_) || +          !t.context ().get<bool> (by_value_key_)); + +        set_dep (t, weak); +      } + +    private: +      void +      set_dep (SemanticGraph::Type& t, bool weak) +      { +        using namespace SemanticGraph; + +        TypeSchemaMap::iterator i (tsm_.find (&t)); + +        // If a type is not present in the map then it must be +        // a built-in type. +        // +        if (i == tsm_.end ()) +          return; + +        // Check if we already saw this type. Theoretically, it could +        // be that we need to upgrade the type of include from weak to +        // strong. But because inheritance is handled first, the type +        // in the set will already be with the right type. +        // +        if (type_set_.find (&t) != type_set_.end ()) +          return; + +        type_set_.insert (&t); + +        Schema& s (*i->second); +        Path path (s.used_begin ()->path ()); +        SemanticGraph::Uses* u; + +        if (s.names_begin ()->name () == schema_.names_begin ()->name ()) +          u = &root_.new_edge<Includes> (schema_, s, path); +        else +          u = &root_.new_edge<Imports> (schema_, s, path); + +        if (weak) +          u->context().set ("weak", true); +      } + +    private: +      SemanticGraph::Schema& schema_; +      SemanticGraph::Schema& root_; +      char const* by_value_key_; +      TypeSchemaMap& tsm_; +      std::set<SemanticGraph::Type*> type_set_; + +      Traversal::Names names_; +    }; +  } + +  namespace Transformations +  { +    SchemaPerType:: +    SchemaPerType (SchemaPerTypeTranslator& trans, +                   bool fat, +                   char const* key) +        : fat_type_file_ (fat), by_value_key_ (key), trans_ (trans) +    { +    } + +    Schemas SchemaPerType:: +    transform (SemanticGraph::Schema& root) +    { +      // Collect initial schema nodes. +      // +      Schemas schemas; +      SemanticGraph::Schema* xsd; + +      { +        Traversal::Schema schema; +        Uses uses (schemas, xsd); + +        schema >> uses >> schema; + +        // Some twisted schemas do recusive inclusions. +        // +        root.context ().set ("xsd-frontend-schema-per-type-seen", true); + +        schema.dispatch (root); +      } + +      // wcerr << schemas.size () << " initial schema nodes" << endl; + +      // Add the schema file names to the file set. +      // +      FileSet file_set; + +      for (Schemas::iterator i (schemas.begin ()); i != schemas.end (); ++i) +      { +        // This path was already normalized by the parser. +        // +        SemanticGraph::Path const& path ( +          (*i)->context ().get<SemanticGraph::Path> ("absolute-path")); + +        // Translate the schema file name. +        // +        NarrowString abs_path; + +        // Try to use the portable representation of the path. If that +        // fails, fall back to the native representation. +        // +        try +        { +          abs_path = path.posix_string (); +        } +        catch (SemanticGraph::InvalidPath const&) +        { +          abs_path = path.string (); +        } + +        NarrowString tf (trans_.translate_schema (abs_path)); +        NarrowString file (tf ? tf : path.leaf ().string ()); + +        size_t p (file.rfind ('.')); +        NarrowString ext ( +          p != NarrowString::npos ? NarrowString (file, p) : ""); + +        NarrowString base ( +          p != NarrowString::npos ? NarrowString (file, 0, p) : file); + +        // Make sure it is unique. +        // +        NarrowString new_name (base); + +        for (unsigned long n (1); +             file_set.find (new_name) != file_set.end (); +             ++n) +        { +          std::ostringstream os; +          os << n; +          new_name = base + os.str (); +        } + +        file_set.insert (new_name); +        new_name += ext; + +        try +        { +          (*i)->context ().set ("renamed", SemanticGraph::Path (new_name)); +        } +        catch (SemanticGraph::InvalidPath const&) +        { +          wcerr << "error: '" << new_name.c_str () << "' is not a valid " +                << "filesystem path" << endl; + +          wcerr << "info: use schema file name translation mechanism " +                << "to resolve this" << endl; + +          throw Failed (); +        } +      } + +      // Process each schema node. +      // +      TypeSchemaMap tsm; + +      for (Schemas::iterator i (schemas.begin ()); i != schemas.end (); ++i) +      { +        process_schema (**i, root, *xsd, tsm, file_set, fat_type_file_, trans_); +      } + +      // wcerr << tsm.size () << " type schema nodes" << endl; + +      // Establish include/import dependencies. While at it add the +      // new schemas to the list which we will return. +      // +      for (TypeSchemaMap::iterator i (tsm.begin ()); i != tsm.end (); ++i) +      { +        SemanticGraph::Schema& s (*i->second); +        Type t (s, root, by_value_key_, tsm); +        t.dispatch (*i->first); +        schemas.push_back (&s); +      } + +      return schemas; +    } + +    SchemaPerTypeTranslator:: +    ~SchemaPerTypeTranslator () +    { +    } +  } +} diff --git a/libxsd-frontend/xsd-frontend/transformations/schema-per-type.hxx b/libxsd-frontend/xsd-frontend/transformations/schema-per-type.hxx new file mode 100644 index 0000000..8b6a69e --- /dev/null +++ b/libxsd-frontend/xsd-frontend/transformations/schema-per-type.hxx @@ -0,0 +1,62 @@ +// file      : xsd-frontend/transformations/schema-per-type.hxx +// copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +// license   : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef XSD_FRONTEND_TRANSFORMATIONS_SCHEMA_PER_TYPE_HXX +#define XSD_FRONTEND_TRANSFORMATIONS_SCHEMA_PER_TYPE_HXX + +#include <vector> + +#include <xsd-frontend/types.hxx> + +#include <xsd-frontend/semantic-graph/elements.hxx> // Path +#include <xsd-frontend/semantic-graph/schema.hxx> + +namespace XSDFrontend +{ +  namespace Transformations +  { +    class SchemaPerTypeTranslator +    { +    public: +      virtual +      ~SchemaPerTypeTranslator (); + +      // The following two functions should return empty string if +      // there is no match. +      // +      virtual String +      translate_type (String const& ns, String const& name) = 0; + +      virtual NarrowString +      translate_schema (NarrowString const& abs_path) = 0; +    }; + +    // This transformation restructures the semantic graph to have +    // each type definition in a seperate schema file. +    // +    class SchemaPerType +    { +    public: +      struct Failed {}; + +      // If a type of an element or attribute has a context entry +      // with the by_value_key key and it is true, then the schema +      // for this type is included "strongly". +      // +      SchemaPerType (SchemaPerTypeTranslator&, +                     bool fat_type_file, +                     char const* by_value_key = 0); + +      std::vector<SemanticGraph::Schema*> +      transform (SemanticGraph::Schema&); + +    private: +      bool fat_type_file_; +      char const* by_value_key_; +      SchemaPerTypeTranslator& trans_; +    }; +  } +} + +#endif // XSD_FRONTEND_TRANSFORMATIONS_SCHEMA_PER_TYPE_HXX diff --git a/libxsd-frontend/xsd-frontend/transformations/simplifier.cxx b/libxsd-frontend/xsd-frontend/transformations/simplifier.cxx new file mode 100644 index 0000000..9372a4a --- /dev/null +++ b/libxsd-frontend/xsd-frontend/transformations/simplifier.cxx @@ -0,0 +1,162 @@ +// file      : xsd-frontend/transformations/simplifier.cxx +// copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +// license   : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#include <xsd-frontend/semantic-graph.hxx> +#include <xsd-frontend/traversal.hxx> + +#include <xsd-frontend/transformations/simplifier.hxx> + +namespace XSDFrontend +{ +  namespace +  { +    struct Compositor: Traversal::All, +                       Traversal::Choice, +                       Traversal::Sequence +    { +      Compositor (SemanticGraph::Schema& root) +          : root_ (root) +      { +      } + +      virtual void +      traverse (SemanticGraph::All& a) +      { +        // The all compositor cannot contain compositors. +        // +        if (a.contains_begin () == a.contains_end ()) +          remove (a); +      } + +      virtual void +      traverse (SemanticGraph::Choice& c) +      { +        // Do the depth-first traversal so that we take into account +        // the potential removal of nested compositors. +        // +        using SemanticGraph::Compositor; + +        for (Compositor::ContainsIterator i (c.contains_begin ()); +             i != c.contains_end ();) +        { +          edge_traverser ().dispatch (*i++); +        } + +        Choice::contains (c); + +        if (c.contains_begin () == c.contains_end ()) +          remove (c); +      } + +      virtual void +      traverse (SemanticGraph::Sequence& s) +      { +        // Do the depth-first traversal so that we take into account +        // the potential removal of nested compositors. +        // +        using SemanticGraph::Compositor; + +        for (Compositor::ContainsIterator i (s.contains_begin ()); +             i != s.contains_end ();) +        { +          edge_traverser ().dispatch (*i++); +        } + +        if (s.contains_begin () == s.contains_end ()) +          remove (s); +      } + +    private: +      virtual void +      remove (SemanticGraph::Compositor& c) +      { +        using SemanticGraph::Node; +        using SemanticGraph::Choice; +        using SemanticGraph::Complex; +        using SemanticGraph::Compositor; + +        if (c.contained_particle_p ()) +        { +          Compositor& com (c.contained_particle ().compositor ()); + +          // Empty compositors in choice are important. +          // +          if (!com.is_a<Choice> ()) +            root_.delete_edge (com, c, c.contained_particle ()); +        } +        else +        { +          Complex& con ( +            dynamic_cast<Complex&> (c.contained_compositor ().container ())); +          root_.delete_edge (con, c, c.contained_compositor ()); +        } +      } + +    private: +      SemanticGraph::Schema& root_; +    }; + +    // +    // +    struct Type: Traversal::Complex +    { +      virtual void +      traverse (SemanticGraph::Complex& c) +      { +        if (c.contains_compositor_p ()) +          Complex::contains_compositor (c); +      } +    }; + +    // 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-simplifier-seen")) +        { +          s.context ().set ("xsd-frontend-simplifier-seen", true); +          Traversal::Uses::traverse (u); +        } +      } +    }; +  } + +  namespace Transformations +  { +    void Simplifier:: +    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; +      Type type; + +      schema >> schema_names >> ns >> ns_names >> type; + +      Compositor compositor (s); +      Traversal::ContainsCompositor contains_compositor; +      Traversal::ContainsParticle contains_particle; + +      type >> contains_compositor >> compositor; +      compositor >> contains_particle >> compositor; + +      // Some twisted schemas do recusive inclusions. +      // +      s.context ().set ("xsd-frontend-simplifier-seen", true); + +      schema.dispatch (s); +    } +  } +} diff --git a/libxsd-frontend/xsd-frontend/transformations/simplifier.hxx b/libxsd-frontend/xsd-frontend/transformations/simplifier.hxx new file mode 100644 index 0000000..674ee45 --- /dev/null +++ b/libxsd-frontend/xsd-frontend/transformations/simplifier.hxx @@ -0,0 +1,30 @@ +// file      : xsd-frontend/transformations/simplifier.hxx +// copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +// license   : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef XSD_FRONTEND_TRANSFORMATIONS_SIMPLIFIER_HXX +#define XSD_FRONTEND_TRANSFORMATIONS_SIMPLIFIER_HXX + +#include <xsd-frontend/types.hxx> + +#include <xsd-frontend/semantic-graph/elements.hxx> // Path +#include <xsd-frontend/semantic-graph/schema.hxx> + +namespace XSDFrontend +{ +  namespace Transformations +  { +    // This transformation performs various schema simplifications +    // (e.g., removing empty compositors, etc). This transformation +    // assumes that there are no anonymous types. +    // +    class Simplifier +    { +    public: +      void +      transform (SemanticGraph::Schema&, SemanticGraph::Path const&); +    }; +  } +} + +#endif // XSD_FRONTEND_TRANSFORMATIONS_SIMPLIFIER_HXX | 
