diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2010-04-15 13:47:31 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2010-04-15 13:47:31 +0200 |
commit | 4c174428379af308926ec70bc5b58539a1863abf (patch) | |
tree | 0f2aeaa68b28e0c86925fd4ae0e2d893d020efdc | |
parent | 8b5538ed49925aaf9fa22e961bd632bed8458184 (diff) |
Implement enum synthesis from union
A union which has enumerations with a common base as members can
be transformed to an equivalent enumeration.
-rw-r--r-- | tests/dump/driver.cxx | 12 | ||||
-rw-r--r-- | tests/schema/enumeration/makefile | 35 | ||||
-rw-r--r-- | tests/schema/enumeration/test-000.std | 62 | ||||
-rw-r--r-- | tests/schema/enumeration/test-000.xsd | 72 | ||||
-rw-r--r-- | tests/schema/makefile | 9 | ||||
-rw-r--r-- | xsd-frontend/makefile | 1 | ||||
-rw-r--r-- | xsd-frontend/semantic-graph/elements.hxx | 2 | ||||
-rw-r--r-- | xsd-frontend/transformations/enum-synthesis.cxx | 249 | ||||
-rw-r--r-- | xsd-frontend/transformations/enum-synthesis.hxx | 33 |
9 files changed, 474 insertions, 1 deletions
diff --git a/tests/dump/driver.cxx b/tests/dump/driver.cxx index 973a30a..2e55e97 100644 --- a/tests/dump/driver.cxx +++ b/tests/dump/driver.cxx @@ -7,6 +7,7 @@ #include <xsd-frontend/parser.hxx> #include <xsd-frontend/transformations/anonymous.hxx> +#include <xsd-frontend/transformations/enum-synthesis.hxx> #include <xsd-frontend/semantic-graph.hxx> #include <xsd-frontend/traversal.hxx> @@ -571,11 +572,14 @@ main (Int argc, Char* argv[]) // Int i (1); Boolean anon (false); + Boolean enum_synth (false); for (; i < argc; ++i) { if (argv[i] == NarrowString ("--anonymous")) anon = true; + else if (argv[i] == NarrowString ("--enum-synthesis")) + enum_synth = true; else break; } @@ -607,6 +611,14 @@ main (Int argc, Char* argv[]) // // + if (enum_synth) + { + Transformations::EnumSynthesis transf; + transf.transform (*tu, path); + } + + // + // Schema schema; Uses uses; diff --git a/tests/schema/enumeration/makefile b/tests/schema/enumeration/makefile new file mode 100644 index 0000000..d958178 --- /dev/null +++ b/tests/schema/enumeration/makefile @@ -0,0 +1,35 @@ +# file : tests/schema/enumeration/makefile +# 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 $(dir $(lastword $(MAKEFILE_LIST)))../../../build/bootstrap.make + +tests := 000 + +driver := $(out_root)/tests/dump/driver +test := $(out_base)/.test +clean := $(out_base)/.clean + +# Convenience alias for default target. +# +$(out_base)/: $(driver) + +# Test. +# +test_targets := $(addprefix $(out_base)/.test-,$(tests)) + +$(test): $(test_targets) +$(test_targets): driver := $(driver) + +.PHONY: $(out_base)/.test-% +$(out_base)/.test-%: $(driver) $(src_base)/test-%.xsd $(src_base)/test-%.std + $(call message,test $(out_base)/$*,$(driver) --enum-synthesis $(src_base)/test-$*.xsd | diff -u $(src_base)/test-$*.std -) + +# Clean. +# +$(clean): + +# Dependencies. +# +$(call import,$(src_root)/tests/dump/makefile) diff --git a/tests/schema/enumeration/test-000.std b/tests/schema/enumeration/test-000.std new file mode 100644 index 0000000..64e3065 --- /dev/null +++ b/tests/schema/enumeration/test-000.std @@ -0,0 +1,62 @@ +primary +{ + namespace test + { + complex common-base: http://www.w3.org/2001/XMLSchema#string + { + } + complex base: test#common-base + { + } + enumeration one: test#base + { + <romance documentation> + enumerator romance + enumerator fiction + enumerator horror + } + enumeration two: test#common-base + { + enumerator horror + enumerator history + enumerator philosophy + } + enumeration three: http://www.w3.org/2001/XMLSchema#anyURI + { + enumerator foo + enumerator bar + } + enumeration union0: test#common-base + { + <romance documentation> + enumerator romance + enumerator fiction + enumerator horror + enumerator history + enumerator philosophy + } + <union1 documentation> + enumeration union1: test#common-base + { + <romance documentation> + enumerator romance + enumerator fiction + enumerator horror + enumerator history + enumerator philosophy + } + union union2 test#one test#union1 test#common-base + union union3 test#one test#three + complex complex1 + { + element a + [1, 1] sequence + { + [1, 1] element a test#union1 + } + } + complex complex2: test#union1 + { + } + } +} diff --git a/tests/schema/enumeration/test-000.xsd b/tests/schema/enumeration/test-000.xsd new file mode 100644 index 0000000..08539bf --- /dev/null +++ b/tests/schema/enumeration/test-000.xsd @@ -0,0 +1,72 @@ +<?xml version="1.0"?> +<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:t="test" targetNamespace="test"> + + <!-- Enumeration synthesis --> + + <simpleType name="common-base"> + <restriction base="string"/> + </simpleType> + + <simpleType name="base"> + <restriction base="t:common-base"/> + </simpleType> + + <simpleType name="one"> + <restriction base="t:base"> + <enumeration value="romance"> + <annotation> + <documentation>romance documentation</documentation> + </annotation> + </enumeration> + <enumeration value="fiction"/> + <enumeration value="horror"/> + </restriction> + </simpleType> + + <simpleType name="two"> + <restriction base="t:common-base"> + <enumeration value="horror"/> + <enumeration value="history"/> + <enumeration value="philosophy"/> + </restriction> + </simpleType> + + <simpleType name="three"> + <restriction base="anyURI"> + <enumeration value="foo"/> + <enumeration value="bar"/> + </restriction> + </simpleType> + + <simpleType name="union0"> + <union memberTypes="t:one t:two t:union1"/> + </simpleType> + + <simpleType name="union1"> + <annotation> + <documentation>union1 documentation</documentation> + </annotation> + <union memberTypes="t:one t:two"/> + </simpleType> + + <simpleType name="union2"> + <union memberTypes="t:one t:union1 t:common-base"/> + </simpleType> + + <simpleType name="union3"> + <union memberTypes="t:one t:three"/> + </simpleType> + + <complexType name="complex1"> + <sequence> + <element name="a" type="t:union1"/> + </sequence> + </complexType> + + <complexType name="complex2"> + <simpleContent> + <extension base="t:union1"/> + </simpleContent> + </complexType> + +</schema> diff --git a/tests/schema/makefile b/tests/schema/makefile index b0fab92..fde5482 100644 --- a/tests/schema/makefile +++ b/tests/schema/makefile @@ -5,7 +5,14 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../../build/bootstrap.make -tests := annotation anonymous attribute-group element-group default union +tests := \ +annotation \ +anonymous \ +attribute-group \ +default \ +element-group \ +enumeration \ +union default := $(out_base)/ test := $(out_base)/.test diff --git a/xsd-frontend/makefile b/xsd-frontend/makefile index e0526ab..abf6565 100644 --- a/xsd-frontend/makefile +++ b/xsd-frontend/makefile @@ -39,6 +39,7 @@ cxx_tun += traversal/attribute.cxx \ traversal/union.cxx cxx_tun += transformations/anonymous.cxx \ + transformations/enum-synthesis.cxx \ transformations/restriction.cxx \ transformations/schema-per-type.cxx \ transformations/simplifier.cxx diff --git a/xsd-frontend/semantic-graph/elements.hxx b/xsd-frontend/semantic-graph/elements.hxx index ec425f6..f59961b 100644 --- a/xsd-frontend/semantic-graph/elements.hxx +++ b/xsd-frontend/semantic-graph/elements.hxx @@ -850,6 +850,8 @@ namespace XSDFrontend } protected: + friend class Bits::Graph<Node, Edge>; + Inherits () { } 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); + } + } +} diff --git a/xsd-frontend/transformations/enum-synthesis.hxx b/xsd-frontend/transformations/enum-synthesis.hxx new file mode 100644 index 0000000..e3c38c7 --- /dev/null +++ b/xsd-frontend/transformations/enum-synthesis.hxx @@ -0,0 +1,33 @@ +// file : xsd-frontend/transformations/enum-synthesis.hxx +// author : Boris Kolpackov <boris@codesynthesis.com> +// copyright : Copyright (c) 2006-2010 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 <cult/types.hxx> + +#include <xsd-frontend/semantic-graph/elements.hxx> // Path +#include <xsd-frontend/semantic-graph/schema.hxx> + +namespace XSDFrontend +{ + namespace Transformations + { + using namespace Cult::Types; + + // 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 |