From 884dea7531962b17ef843ac2175faa050e8b0758 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 10 Apr 2014 12:57:06 +0200 Subject: Add support for ordered types, mixed content --- NEWS | 10 + dist/examples/cxx/tree/makefile | 2 +- dist/examples/cxx/tree/order/element/makefile | 31 ++ dist/examples/cxx/tree/order/makefile | 11 + dist/examples/cxx/tree/order/mixed/makefile | 32 ++ doc/cxx/tree/guide/index.xhtml | 23 +- doc/cxx/tree/manual/index.xhtml | 665 ++++++++++++++++++++++- doc/xsd-epilogue.1 | 5 + doc/xsd-epilogue.xhtml | 5 + examples/cxx/tree/README | 8 +- examples/cxx/tree/makefile | 7 +- examples/cxx/tree/mixed/README | 6 +- examples/cxx/tree/mixed/text.xml | 2 +- examples/cxx/tree/order/README | 11 + examples/cxx/tree/order/element/README | 35 ++ examples/cxx/tree/order/element/driver.cxx | 147 +++++ examples/cxx/tree/order/element/makefile | 100 ++++ examples/cxx/tree/order/element/transactions.xml | 33 ++ examples/cxx/tree/order/element/transactions.xsd | 59 ++ examples/cxx/tree/order/makefile | 44 ++ examples/cxx/tree/order/mixed/README | 45 ++ examples/cxx/tree/order/mixed/driver.cxx | 89 +++ examples/cxx/tree/order/mixed/makefile | 99 ++++ examples/cxx/tree/order/mixed/text.xml | 18 + examples/cxx/tree/order/mixed/text.xsd | 29 + libxsd/xsd/cxx/tree/elements.hxx | 40 +- libxsd/xsd/cxx/tree/elements.ixx | 21 + libxsd/xsd/cxx/xml/dom/parsing-source.hxx | 33 +- libxsd/xsd/cxx/xml/dom/parsing-source.txx | 44 +- tests/cxx/tree/makefile | 1 + tests/cxx/tree/order/driver.cxx | 65 +++ tests/cxx/tree/order/makefile | 94 ++++ tests/cxx/tree/order/output | 92 ++++ tests/cxx/tree/order/test.xml | 71 +++ tests/cxx/tree/order/test.xsd | 130 +++++ tests/cxx/tree/wildcard/driver.cxx | 3 + xsd/cxx/tree/elements.hxx | 71 ++- xsd/cxx/tree/fundamental-header.hxx | 11 + xsd/cxx/tree/generator.cxx | 10 + xsd/cxx/tree/name-processor.cxx | 285 +++++++++- xsd/cxx/tree/options.cli | 60 ++ xsd/cxx/tree/order-processor.cxx | 244 +++++++++ xsd/cxx/tree/order-processor.hxx | 30 + xsd/cxx/tree/polymorphism-processor.cxx | 64 +-- xsd/cxx/tree/serialization-source.cxx | 219 ++++++-- xsd/cxx/tree/tree-header.cxx | 369 ++++++++++++- xsd/cxx/tree/tree-inline.cxx | 74 ++- xsd/cxx/tree/tree-source.cxx | 251 ++++++++- xsd/makefile | 2 + xsd/options-parser.hxx | 3 +- 50 files changed, 3599 insertions(+), 204 deletions(-) create mode 100644 dist/examples/cxx/tree/order/element/makefile create mode 100644 dist/examples/cxx/tree/order/makefile create mode 100644 dist/examples/cxx/tree/order/mixed/makefile create mode 100644 examples/cxx/tree/order/README create mode 100644 examples/cxx/tree/order/element/README create mode 100644 examples/cxx/tree/order/element/driver.cxx create mode 100644 examples/cxx/tree/order/element/makefile create mode 100644 examples/cxx/tree/order/element/transactions.xml create mode 100644 examples/cxx/tree/order/element/transactions.xsd create mode 100644 examples/cxx/tree/order/makefile create mode 100644 examples/cxx/tree/order/mixed/README create mode 100644 examples/cxx/tree/order/mixed/driver.cxx create mode 100644 examples/cxx/tree/order/mixed/makefile create mode 100644 examples/cxx/tree/order/mixed/text.xml create mode 100644 examples/cxx/tree/order/mixed/text.xsd create mode 100644 tests/cxx/tree/order/driver.cxx create mode 100644 tests/cxx/tree/order/makefile create mode 100644 tests/cxx/tree/order/output create mode 100644 tests/cxx/tree/order/test.xml create mode 100644 tests/cxx/tree/order/test.xsd create mode 100644 xsd/cxx/tree/order-processor.cxx create mode 100644 xsd/cxx/tree/order-processor.hxx diff --git a/NEWS b/NEWS index 54a956b..bb220b9 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,16 @@ Version 4.0.0 C++/Tree + * Support for ordered types. C++/Tree flattens nested compositors + which sometimes can result in loss of element ordering information + that could be significant to the application. Ordered types address + this problem. For more information, refer to Section 2.8.4, "Element + Order" in the C++/Tree Mapping User Manual. + + * Support for mixed content in ordered types. For more information, refer + to Section 2.13, "Mapping for Mixed Content Models" in the C++/Tree + Mapping User Manual. + * New option, --suppress-assignment, suppress the generation of copy assignment operators for complex types. If this option is specified, the copy assignment operators for such types are declared private diff --git a/dist/examples/cxx/tree/makefile b/dist/examples/cxx/tree/makefile index 70226f9..44e7a0f 100644 --- a/dist/examples/cxx/tree/makefile +++ b/dist/examples/cxx/tree/makefile @@ -1,5 +1,5 @@ dirs := binary caching embedded custom hello library messaging mixed \ -multiroot performance polymorphism streaming wildcard +multiroot order performance polymorphism streaming wildcard ifeq ($(WITH_ZLIB),1) dirs += compression diff --git a/dist/examples/cxx/tree/order/element/makefile b/dist/examples/cxx/tree/order/element/makefile new file mode 100644 index 0000000..31f5ec5 --- /dev/null +++ b/dist/examples/cxx/tree/order/element/makefile @@ -0,0 +1,31 @@ +root := ../../../.. + +include $(root)/build/cxx/rules.make +include $(root)/build/xsd/tree-rules.make + + +override XSDFLAGS += --generate-serialization --generate-wildcard --ordered-type batch + + +# Build. +# +driver: driver.o transactions.o + +transactions.o: transactions.cxx transactions.hxx +driver.o: driver.cxx transactions.hxx + +transactions.cxx transactions.hxx: transactions.xsd + + +# Test +# +.PHONY: test +test: driver transactions.xml + ./driver transactions.xml + + +# Clean. +# +.PHONY: clean +clean: + rm -f transactions.o transactions.?xx driver.o driver diff --git a/dist/examples/cxx/tree/order/makefile b/dist/examples/cxx/tree/order/makefile new file mode 100644 index 0000000..2713b6e --- /dev/null +++ b/dist/examples/cxx/tree/order/makefile @@ -0,0 +1,11 @@ +dirs := element mixed + +.PHONY: all $(dirs) + +all: $(dirs) + +$(dirs): + @$(MAKE) -C $@ $(MAKECMDGOALS) + +makefile: ; +% :: $(dirs) ; diff --git a/dist/examples/cxx/tree/order/mixed/makefile b/dist/examples/cxx/tree/order/mixed/makefile new file mode 100644 index 0000000..b7929d9 --- /dev/null +++ b/dist/examples/cxx/tree/order/mixed/makefile @@ -0,0 +1,32 @@ +root := ../../../.. + +include $(root)/build/cxx/rules.make +include $(root)/build/xsd/tree-rules.make + + +override XSDFLAGS += --generate-serialization --ordered-type-mixed + + +# Build. +# +driver: driver.o text.o + +text.o: text.cxx text.hxx +driver.o: driver.cxx text.hxx + +text.cxx text.hxx: text.xsd + + +# Test +# +.PHONY: test +test: driver text.xml + ./driver text.xml + + +# Clean. +# +.PHONY: clean +clean: + rm -f text.o text.?xx driver.o driver + diff --git a/doc/cxx/tree/guide/index.xhtml b/doc/cxx/tree/guide/index.xhtml index 5c03280..49ad3a6 100644 --- a/doc/cxx/tree/guide/index.xhtml +++ b/doc/cxx/tree/guide/index.xhtml @@ -1558,18 +1558,31 @@ class people_t container. The modifier functions copies the entries from the passed sequence.

+

C++/Tree is a "flattening" mapping in a sense that many levels of + nested compositors (choice and sequence), + all potentially with their own cardinalities, are in the end mapped + to a flat set of elements with one of the three cardinality classes + discussed above. While this results in a simple and easy to use API + for most types, in certain cases, the order of elements in the actual + XML documents is not preserved once parsed into the object model. To + overcome this limitation we can mark certain schema types, for which + content order is not sufficiently preserved, as ordered. For more + information on this functionality refer to + Section + 2.8.4, "Element Order" in the C++/Tree Mapping User Manual.

+

For complex schemas with many levels of nested compositors - (xs:choice and xs:sequence) it can + (choice and sequence) it can also be hard to deduce the cardinality class of a particular element. The generated Doxygen documentation can greatly help with this task. For each element and attribute the documentation clearly identifies its cardinality class. Alternatively, you can study the generated header files to find out the cardinality - class of a particular attribute or element. In the next sections - we will examine how to access and modify information stored in - an object model using accessor and modifier functions described - in this section.

+ class of a particular attribute or element.

+

In the next sections we will examine how to access and modify + information stored in an object model using accessor and modifier + functions described in this section.

4.2 Accessing the Object Model

diff --git a/doc/cxx/tree/manual/index.xhtml b/doc/cxx/tree/manual/index.xhtml index 85a5f83..052f2b3 100644 --- a/doc/cxx/tree/manual/index.xhtml +++ b/doc/cxx/tree/manual/index.xhtml @@ -298,6 +298,7 @@ 2.8.1Mapping for Members with the One Cardinality Class 2.8.2Mapping for Members with the Optional Cardinality Class 2.8.3Mapping for Members with the Sequence Cardinality Class + 2.8.4Element Order @@ -321,7 +322,8 @@ 2.12.1Mapping for any with the One Cardinality Class 2.12.2Mapping for any with the Optional Cardinality Class 2.12.3Mapping for any with the Sequence Cardinality Class - 2.12.4Mapping for anyAttribute + 2.12.4Element Wildcard Order + 2.12.5Mapping for anyAttribute @@ -2237,7 +2239,7 @@ public:

is mapped to:

-class color: xml_schema::string
+class color: public xml_schema::string
 {
 public:
   enum value
@@ -2423,7 +2425,7 @@ public:
   

is mapped to:

-class complex: xml_schema::type
+class complex: public xml_schema::type
 {
 public:
   object (const int& a, const xml_schema::string& b);
@@ -2441,7 +2443,7 @@ public:
 
 };
 
-class object: xml_schema::type
+class object: public xml_schema::type
 {
 public:
   object (const bool& s_one, const complex& c_one);
@@ -2484,7 +2486,7 @@ public:
   

is mapped to:

-class object: xml_schema::string
+class object: public xml_schema::string
 {
 public:
   object (const xml_schema::language& lang);
@@ -2529,7 +2531,7 @@ public:
   

is mapped to:

-class color: xml_schema::string
+class color: public xml_schema::string
 {
 public:
   enum value
@@ -2666,7 +2668,7 @@ public:
   

is mapped to:

-class object: xml_schema::type
+class object: public xml_schema::type
 {
 public:
   typedef xml_schema::string member_type;
@@ -2692,7 +2694,7 @@ public:
   

is mapped to:

-class object: xml_schema::type
+class object: public xml_schema::type
 {
 public:
   typedef xml_schema::string data_type;
@@ -2749,7 +2751,7 @@ public:
   

is mapped to:

-class object: xml_schema::type
+class object: public xml_schema::type
 {
 public:
   // Type definitions.
@@ -2782,7 +2784,7 @@ public:
       member's type, for example:

-class object: xml_schema::type
+class object: public xml_schema::type
 {
 public:
   ...
@@ -2873,7 +2875,7 @@ f (object& o)
   

is mapped to:

-class object: xml_schema::type
+class object: public xml_schema::type
 {
 public:
   // Type definitions.
@@ -3102,7 +3104,7 @@ f (object& o)
   

is mapped to:

-class object: xml_schema::type
+class object: public xml_schema::type
 {
 public:
   // Type definitions.
@@ -3221,6 +3223,415 @@ f (object& o)
 }
   
+

2.8.4 Element Order

+ +

C++/Tree is a "flattening" mapping in a sense that many levels of + nested compositors (choice and sequence), + all potentially with their own cardinalities, are in the end mapped + to a flat set of elements with one of the three cardinality classes + discussed in the previous sections. While this results in a simple + and easy to use API for most types, in certain cases, the order of + elements in the actual XML documents is not preserved once parsed + into the object model. And sometimes such order has + application-specific significance. As an example, consider a schema + that defines a batch of bank transactions:

+ +
+<complexType name="withdraw">
+  <sequence>
+    <element name="account" type="unsignedInt"/>
+    <element name="amount" type="unsignedInt"/>
+  </sequence>
+</complexType>
+
+<complexType name="deposit">
+  <sequence>
+    <element name="account" type="unsignedInt"/>
+    <element name="amount" type="unsignedInt"/>
+  </sequence>
+</complexType>
+
+<complexType name="batch">
+  <choice minOccurs="0" maxOccurs="unbounded">
+    <element name="withdraw" type="withdraw"/>
+    <element name="deposit" type="deposit"/>
+  </choice>
+</complexType>
+  
+ +

The batch can contain any number of transactions in any order + but the order of transactions in each actual batch is significant. + For instance, consider what could happen if we reorder the + transactions and apply all the withdrawals before deposits.

+ +

For the batch schema type defined above the default + C++/Tree mapping will produce a C++ class that contains a pair of + sequence containers, one for each of the two elements. While this + will capture the content (transactions), the order of this content + as it appears in XML will be lost. Also, if we try to serialize the + batch we just loaded back to XML, all the withdrawal transactions + will appear before deposits.

+ +

To overcome this limitation of a flattening mapping, C++/Tree + allows us to mark certain XML Schema types, for which content + order is important, as ordered.

+ +

There are several command line options that control which + schema types are treated as ordered. To make an individual + type ordered, we use the --ordered-type option, + for example:

+ +
+--ordered-type batch
+  
+ +

To automatically treat all the types that are derived from an ordered + type also ordered, we use the --ordered-type-derived + option. This is primarily useful if you would like to iterate + over the complete hierarchy's content using the content order + sequence (discussed below).

+ +

Ordered types are also useful for handling mixed content. To + automatically mark all the types with mixed content as ordered + we use the --ordered-type-mixed option. For more + information on handling mixed content see Section + 2.13, "Mapping for Mixed Content Models".

+ +

Finally, we can mark all the types in the schema we are + compiling with the --ordered-type-all option. + You should only resort to this option if all the types in + your schema truly suffer from the loss of content + order since, as we will discuss shortly, ordered types + require extra effort to access and, especially, modify. + See the + XSD + Compiler Command Line Manual for more information on + these options.

+ +

Once a type is marked ordered, C++/Tree alters its mapping + in several ways. Firstly, for each local element, element + wildcard (Section 2.12.4, "Element Wildcard + Order"), and mixed content text (Section + 2.13, "Mapping for Mixed Content Models") in this type, a + content id constant is generated. Secondly, an addition sequence + is added to the class that captures the content order. Here + is how the mapping of our batch class changes + once we make it ordered:

+ +
+class batch: public xml_schema::type
+{
+public:
+  // withdraw
+  //
+  typedef withdraw withdraw_type;
+  typedef sequence<withdraw_type> withdraw_sequence;
+  typedef withdraw_sequence::iterator withdraw_iterator;
+  typedef withdraw_sequence::const_iterator withdraw_const_iterator;
+
+  static const std::size_t withdraw_id = 1;
+
+  const withdraw_sequence&
+  withdraw () const;
+
+  withdraw_sequence&
+  withdraw ();
+
+  void
+  withdraw (const withdraw_sequence&);
+
+  // deposit
+  //
+  typedef deposit deposit_type;
+  typedef sequence<deposit_type> deposit_sequence;
+  typedef deposit_sequence::iterator deposit_iterator;
+  typedef deposit_sequence::const_iterator deposit_const_iterator;
+
+  static const std::size_t deposit_id = 2;
+
+  const deposit_sequence&
+  deposit () const;
+
+  deposit_sequence&
+  deposit ();
+
+  void
+  deposit (const deposit_sequence&);
+
+  // content_order
+  //
+  typedef xml_schema::content_order content_order_type;
+  typedef std::vector<content_order_type> content_order_sequence;
+  typedef content_order_sequence::iterator content_order_iterator;
+  typedef content_order_sequence::const_iterator content_order_const_iterator;
+
+  const content_order_sequence&
+  content_order () const;
+
+  content_order_sequence&
+  content_order ();
+
+  void
+  content_order (const content_order_sequence&);
+
+  ...
+};
+  
+ +

Notice the withdraw_id and deposit_id + content ids as well as the extra content_order + sequence that does not correspond to any element in the + schema definition. The other changes to the mapping for ordered + types has to do with XML parsing and serialization code. During + parsing the content order is captured in the content_order + sequence while during serialization this sequence is used to + determine the order in which content is serialized. The + content_order sequence is also copied during + copy construction and assigned during copy assignment. It is also + taken into account during comparison.

+ +

The entry type of the content_order sequence is the + xml_schema::content_order type that has the following + interface:

+ +
+namespace xml_schema
+{
+  struct content_order
+  {
+    content_order (std::size_t id, std::size_t index = 0);
+
+    std::size_t id;
+    std::size_t index;
+  };
+
+  bool
+  operator== (const content_order&, const content_order&);
+
+  bool
+  operator!= (const content_order&, const content_order&);
+
+  bool
+  operator< (const content_order&, const content_order&);
+}
+  
+ +

The content_order sequence describes the order of + content (elements, including wildcards, as well as mixed content + text). Each entry in this sequence consists of the content id + (for example, withdraw_id or deposit_id + in our case) as well as, for elements of the sequence cardinality + class, an index into the corresponding sequence container (the + index is unused for the one and optional cardinality classes). + For example, in our case, if the content id is withdraw_id, + then the index will point into the withdraw element + sequence.

+ +

With all this information we can now examine how to iterate over + transaction in the batch in content order:

+ +
+batch& b = ...
+
+for (batch::content_order_const_iterator i (b.content_order ().begin ());
+     i != b.content_order ().end ();
+     ++i)
+{
+  switch (i->id)
+  {
+  case batch::withdraw_id:
+    {
+      const withdraw& t (b.withdraw ()[i->index]);
+      cerr << t.account () << " withdraw " << t.amount () << endl;
+      break;
+    }
+  case batch::deposit_id:
+    {
+      const deposit& t (b.deposit ()[i->index]);
+      cerr << t.account () << " deposit " << t.amount () << endl;
+      break;
+    }
+  default:
+    {
+      assert (false); // Unknown content id.
+    }
+  }
+}
+  
+ +

If we serialized our batch back to XML, we would also see that the + order of transactions in the output is exactly the same as in the + input rather than all the withdrawals first followed by all the + deposits.

+ +

The most complex aspect of working with ordered types is + modifications. Now we not only need to change the content, + but also remember to update the order information corresponding + to this change. As a first example, we add a deposit transaction + to the batch:

+ +
+using xml_schema::content_order;
+
+batch::deposit_sequence& d (b.deposit ());
+batch::withdraw_sequence& w (b.withdraw ());
+batch::content_order_sequence& co (b.content_order ());
+
+d.push_back (deposit (123456789, 100000));
+co.push_back (content_order (batch::deposit_id, d.size () - 1));
+  
+ +

In the above example we first added the content (deposit + transaction) and then updated the content order information + by adding an entry with deposit_id content + id and the index of the just added deposit transaction.

+ +

Removing the last transaction can be easy if we know which + transaction (deposit or withdrawal) is last:

+ +
+d.pop_back ();
+co.pop_back ();
+  
+ +

If, however, we do not know which transaction is last, then + things get a bit more complicated:

+ +
+switch (co.back ().id)
+{
+case batch::withdraw_id:
+  {
+    d.pop_back ();
+    break;
+  }
+case batch::deposit_id:
+  {
+    w.pop_back ();
+    break;
+  }
+}
+
+co.pop_back ();
+  
+ +

The following example shows how to add a transaction at the + beginning of the batch:

+ +
+w.push_back (withdraw (123456789, 100000));
+co.insert (co.begin (),
+           content_order (batch::withdraw_id, w.size () - 1));
+  
+ +

Note also that when we merely modify the content of one + of the elements in place, we do not need to update its + order since it doesn't change. For example, here is how + we can change the amount in the first withdrawal:

+ +
+w[0].amount (10000);
+  
+ +

For the complete working code shown in this section refer to the + order/element example in the + examples/cxx/tree/ directory in the XSD distribution.

+ +

If both the base and derived types are ordered, then the + content order sequence is only added to the base and the content + ids are unique within the whole hierarchy. In this case + the content order sequence for the derived type contains + ordering information for both base and derived content.

+ +

In some applications we may need to perform more complex + content processing. For example, in our case, we may need + to remove all the withdrawal transactions. The default + container, std::vector, is not particularly + suitable for such operations. What may be required by + some applications is a multi-index container that not + only allows us to iterate in content order similar to + std::vector but also search by the content + id as well as the content id and index pair.

+ +

While C++/Tree does not provide this functionality by + default, it allows us to specify a custom container + type for content order with the --order-container + command line option. The only requirement from the + generated code side for such a container is to provide + the vector-like push_back(), + size(), and const iteration interfaces.

+ +

As an example, here is how we can use the Boost Multi-Index + container for content order. First we create the + content-order-container.hxx header with the + following definition (in C++11, use the alias template + instead):

+ +
+#ifndef CONTENT_ORDER_CONTAINER
+#define CONTENT_ORDER_CONTAINER
+
+#include <cstddef> // std::size_t
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/identity.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/random_access_index.hpp>
+
+struct by_id {};
+struct by_id_index {};
+
+template <typename T>
+struct content_order_container:
+  boost::multi_index::multi_index_container<
+    T,
+    boost::multi_index::indexed_by<
+      boost::multi_index::random_access<>,
+      boost::multi_index::ordered_unique<
+        boost::multi_index::tag<by_id_index>,
+        boost::multi_index::identity<T>
+      >,
+      boost::multi_index::ordered_non_unique<
+        boost::multi_index::tag<by_id>,
+        boost::multi_index::member<T, std::size_t, &T::id>
+      >
+    >
+  >
+{};
+
+#endif
+  
+ +

Next we add the following two XSD compiler options to include + this header into every generated header file and to use the + custom container type (see the XSD compiler command line manual + for more information on shell quoting for the first option):

+ +
+--hxx-prologue '#include "content-order-container.hxx"'
+--order-container content_order_container
+  
+ +

With these changes we can now use the multi-index functionality, + for example, to search for a specific content id:

+ +
+typedef batch::content_order_sequence::index<by_id>::type id_set;
+typedef id_set::iterator id_iterator;
+
+const id_set& ids (b.content_order ().get<by_id> ());
+
+std::pair<id_iterator, id_iterator> r (
+  ids.equal_range (std::size_t (batch::deposit_id));
+
+for (id_iterator i (r.first); i != r.second; ++i)
+{
+  const deposit& t (b.deposit ()[i->index]);
+  cerr << t.account () << " deposit " << t.amount () << endl;
+}
+  
+

2.9 Mapping for Global Elements

An XML Schema element definition is called global if it appears @@ -3729,7 +4140,7 @@ f (root& r)

is mapped to:

-class object: xml_schema::type
+class object: public xml_schema::type
 {
 public:
   // any
@@ -3807,7 +4218,7 @@ public:
   

is mapped to:

-class object: xml_schema::type
+class object: public xml_schema::type
 {
 public:
   // Accessors.
@@ -3888,7 +4299,7 @@ f (object& o, const xercesc::DOMElement& e)
   

is mapped to:

-class object: xml_schema::type
+class object: public xml_schema::type
 {
 public:
   // Type definitions.
@@ -4071,7 +4482,7 @@ f (object& o, const xercesc::DOMElement& e)
   

is mapped to:

-class object: xml_schema::type
+class object: public xml_schema::type
 {
 public:
   // Type definitions.
@@ -4314,8 +4725,84 @@ f (object& o, const xercesc::DOMElement& e)
 }
   
+

2.12.4 Element Wildcard Order

+ +

Similar to elements, element wildcards in ordered types + (Section 2.8.4, "Element Order") are assigned + content ids and are included in the content order sequence. + Continuing with the bank transactions example started in Section + 2.8.4, we can extend the batch by allowing custom transactions:

+ +
+<complexType name="batch">
+  <choice minOccurs="0" maxOccurs="unbounded">
+    <element name="withdraw" type="withdraw"/>
+    <element name="deposit" type="deposit"/>
+    <any namespace="##other" processContents="lax"/>
+  </choice>
+</complexType>
+  
+ +

This will lead to the following changes in the generated + batch C++ class:

+ +
+class batch: public xml_schema::type
+{
+public:
+  ...
+
+  // any
+  //
+  typedef element_sequence any_sequence;
+  typedef any_sequence::iterator any_iterator;
+  typedef any_sequence::const_iterator any_const_iterator;
+
+  static const std::size_t any_id = 3UL;
+
+  const any_sequence&
+  any () const;
+
+  any_sequence&
+  any ();
+
+  void
+  any (const any_sequence&);
+
+  ...
+};
+  
+ +

With this change we also need to update the iteration code to handle + the new content id:

+ +
+for (batch::content_order_const_iterator i (b.content_order ().begin ());
+     i != b.content_order ().end ();
+     ++i)
+{
+  switch (i->id)
+  {
+    ...
+
+  case batch::any_id:
+    {
+      const DOMElement& e (b.any ()[i->index]);
+      ...
+      break;
+    }
+
+    ...
+  }
+}
+  
+ +

For the complete working code that shows the use of wildcards in + ordered types refer to the order/element example in + the examples/cxx/tree/ directory in the XSD + distribution.

-

2.12.4 Mapping for anyAttribute

+

2.12.5 Mapping for anyAttribute

For anyAttribute the type definitions consist of an alias of the container type with name any_attribute_set @@ -4353,7 +4840,7 @@ f (object& o, const xercesc::DOMElement& e)

is mapped to:

-class object: xml_schema::type
+class object: public xml_schema::type
 {
 public:
   // Type definitions.
@@ -4594,14 +5081,138 @@ f (object& o, const xercesc::DOMAttr& a)
 
   

2.13 Mapping for Mixed Content Models

-

XML Schema mixed content models do not have a direct C++ mapping. - Instead, information in XML instance documents, corresponding to - a mixed content model, can be accessed using generic DOM nodes that - can optionally be associated with object model nodes. See - Section 5.1, "DOM Association" for more - information about keeping association with DOM nodes. -

+

For XML Schema types with mixed content models C++/Tree provides + mapping support only if the type is marked as ordered + (Section 2.8.4, "Element Order"). Use the + --ordered-type-mixed XSD compiler option to + automatically mark all types with mixed content as ordered.

+ +

For an ordered type with mixed content, C++/Tree adds an extra + text content sequence that is used to store the text fragments. + This text content sequence is also assigned the content id and + its entries are included in the content order sequence, just + like elements. As a result, it is possible to capture the order + between elements and text fragments.

+ +

As an example, consider the following schema that describes text + with embedded links:

+ +
+<complexType name="anchor">
+  <simpleContent>
+    <extension base="string">
+      <attribute name="href" type="anyURI" use="required"/>
+    </extension>
+  </simpleContent>
+</complexType>
+
+<complexType name="text" mixed="true">
+  <sequence>
+    <element name="a" type="anchor" minOccurs="0" maxOccurs="unbounded"/>
+  </sequence>
+</complexType>
+  
+ +

The generated text C++ class will provide the following + API (assuming it is marked as ordered):

+ +
+class text: public xml_schema::type
+{
+public:
+  // a
+  //
+  typedef anchor a_type;
+  typedef sequence<a_type> a_sequence;
+  typedef a_sequence::iterator a_iterator;
+  typedef a_sequence::const_iterator a_const_iterator;
+
+  static const std::size_t a_id = 1UL;
+
+  const a_sequence&
+  a () const;
+
+  a_sequence&
+  a ();
+
+  void
+  a (const a_sequence&);
+
+  // text_content
+  //
+  typedef xml_schema::string text_content_type;
+  typedef sequence<text_content_type> text_content_sequence;
+  typedef text_content_sequence::iterator text_content_iterator;
+  typedef text_content_sequence::const_iterator text_content_const_iterator;
+
+  static const std::size_t text_content_id = 2UL;
+
+  const text_content_sequence&
+  text_content () const;
+
+  text_content_sequence&
+  text_content ();
+
+  void
+  text_content (const text_content_sequence&);
+
+  // content_order
+  //
+  typedef xml_schema::content_order content_order_type;
+  typedef std::vector<content_order_type> content_order_sequence;
+  typedef content_order_sequence::iterator content_order_iterator;
+  typedef content_order_sequence::const_iterator content_order_const_iterator;
+
+  const content_order_sequence&
+  content_order () const;
+
+  content_order_sequence&
+  content_order ();
+
+  void
+  content_order (const content_order_sequence&);
+
+  ...
+};
+  
+ +

Given this interface we can iterate over both link elements + and text in content order. The following code fragment converts + our format to plain text with references.

+ +
+const text& t = ...
+
+for (text::content_order_const_iterator i (t.content_order ().begin ());
+     i != t.content_order ().end ();
+     ++i)
+{
+  switch (i->id)
+  {
+  case text::a_id:
+    {
+      const anchor& a (t.a ()[i->index]);
+      cerr << a << "[" << a.href () << "]";
+      break;
+    }
+  case text::text_content_id:
+    {
+      const xml_schema::string& s (t.text_content ()[i->index]);
+      cerr << s;
+      break;
+    }
+  default:
+    {
+      assert (false); // Unknown content id.
+    }
+  }
+}
+  
+

For the complete working code that shows the use of mixed content + in ordered types refer to the order/mixed example in + the examples/cxx/tree/ directory in the XSD + distribution.

@@ -5835,7 +6446,7 @@ XMLPlatformUtils::Terminate ();

Maintaining DOM association is normally useful when the application needs access to XML constructs that are not preserved in the - object model, for example, text in the mixed content model. + object model, for example, XML comments. Another useful aspect of DOM association is the ability of the application to navigate the document tree using the generic DOM interface (for example, with the help of an XPath processor) @@ -5845,7 +6456,7 @@ XMLPlatformUtils::Terminate (); be ignored during serialization. If you need to not only access but also modify some aspects of XML that are not preserved in the object model, then type customization with custom parsing - constructs and serialization operators should be used instead.

+ constructors and serialization operators should be used instead.

To request DOM association you will need to pass the xml_schema::flags::keep_dom flag to one of the diff --git a/doc/xsd-epilogue.1 b/doc/xsd-epilogue.1 index c5a2760..2b78ff7 100644 --- a/doc/xsd-epilogue.1 +++ b/doc/xsd-epilogue.1 @@ -21,6 +21,7 @@ options. A custom naming convention can be achieved using the .BR --seq-modifier-regex , .BR --parser-regex , .BR --serializer-regex , +.BR --const-regex , .BR --enumerator-regex , and .B --element-type-regex @@ -96,6 +97,7 @@ The .BR --seq-modifier-regex , .BR --parser-regex , .BR --serializer-regex , +.BR --const-regex , .BR --enumerator-regex , and .B --element-type-regex @@ -234,6 +236,9 @@ naming convention is selected: .B /(.+)/parse\\\\u$1/ +The const category is used to create C++ constant names for the +element/wildcard/text content ids in ordered types. + See also the REGEX AND SHELL QUOTING section below. \" diff --git a/doc/xsd-epilogue.xhtml b/doc/xsd-epilogue.xhtml index 275224f..5f91450 100644 --- a/doc/xsd-epilogue.xhtml +++ b/doc/xsd-epilogue.xhtml @@ -16,6 +16,7 @@ --seq-modifier-regex, --parser-regex, --serializer-regex, + --const-regex, --enumerator-regex, and --element-type-regex options.

@@ -78,6 +79,7 @@ --seq-modifier-regex, --parser-regex, --serializer-regex, + --const-regex, --enumerator-regex, and --element-type-regex options allow you to specify extra regular expressions for each name category in @@ -184,6 +186,9 @@

/(.+)/parse\u$1/

+

The const category is used to create C++ constant names for the + element/wildcard/text content ids in ordered types.

+

See also the REGEX AND SHELL QUOTING section below.

TYPE MAP

diff --git a/examples/cxx/tree/README b/examples/cxx/tree/README index ee91b43..569bf11 100644 --- a/examples/cxx/tree/README +++ b/examples/cxx/tree/README @@ -17,6 +17,10 @@ polymorphism Shows how to use XML Schema polymorphism features such as the xsi:type attribute and substitution groups. +order/ + A collection of examples that show how to use ordered types to + capture and maintain content order. + xpath Shows how to use the C++/Tree mapping together with XPath. @@ -45,7 +49,7 @@ caching embedded Shows how to embed the binary representation of the schema grammar - into an application and then use it with the C++/Tree mapping to + into an application and then use it with the C++/Tree mapping to parse and validate XML documents. performance @@ -68,7 +72,7 @@ streaming that are too large to fit into memory. compression - Shows how to compress an XML document during serialization and decompress + Shows how to compress an XML document during serialization and decompress it during parsing using the zlib library. binary/ diff --git a/examples/cxx/tree/makefile b/examples/cxx/tree/makefile index 728860b..688dfd5 100644 --- a/examples/cxx/tree/makefile +++ b/examples/cxx/tree/makefile @@ -5,12 +5,11 @@ include $(dir $(lastword $(MAKEFILE_LIST)))../../../build/bootstrap.make all_examples := binary caching embedded custom hello library messaging \ -mixed multiroot performance polymorphism streaming wildcard compression \ -xpath - +mixed multiroot order performance polymorphism streaming wildcard \ +compression xpath build_examples := binary caching embedded custom hello library messaging \ -mixed multiroot performance polymorphism streaming wildcard +mixed multiroot order performance polymorphism streaming wildcard ifeq ($(xsd_with_zlib),y) build_examples += compression diff --git a/examples/cxx/tree/mixed/README b/examples/cxx/tree/mixed/README index 9ab3309..fc23faa 100644 --- a/examples/cxx/tree/mixed/README +++ b/examples/cxx/tree/mixed/README @@ -1,8 +1,12 @@ This example shows how to access the underlying DOM nodes in the C++/Tree mapping in order to handle raw, "type-less content" such as mixed content models, anyType/anySimpleType, and any/anyAttribute. + +For an alternative (and recommended) approach that employs ordered +types see the order/mixed example. + For an alternative approach that employes type customization see -examples in the custom/ directory, in particular, custom/mixed and +examples in the custom/ directory, in particular, custom/mixed and custom/wildcard. In this example we use mixed content model to describe text with diff --git a/examples/cxx/tree/mixed/text.xml b/examples/cxx/tree/mixed/text.xml index 69abe8f..9d70397 100644 --- a/examples/cxx/tree/mixed/text.xml +++ b/examples/cxx/tree/mixed/text.xml @@ -2,7 +2,7 @@ + + + + 123456789 + 1000000 + + + + 123456789 + + + + 123456789 + 500000 + + + + 123456789 + 500000 + + diff --git a/examples/cxx/tree/order/element/transactions.xsd b/examples/cxx/tree/order/element/transactions.xsd new file mode 100644 index 0000000..54e9cc2 --- /dev/null +++ b/examples/cxx/tree/order/element/transactions.xsd @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/cxx/tree/order/makefile b/examples/cxx/tree/order/makefile new file mode 100644 index 0000000..63f1f3a --- /dev/null +++ b/examples/cxx/tree/order/makefile @@ -0,0 +1,44 @@ +# file : examples/cxx/tree/order/makefile +# copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +# license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +include $(dir $(lastword $(MAKEFILE_LIST)))../../../../build/bootstrap.make + +all_examples := element mixed +build_examples := element mixed + +default := $(out_base)/ +install := $(out_base)/.install +dist := $(out_base)/.dist +dist-win := $(out_base)/.dist-win +clean := $(out_base)/.clean + +# Build. +# +$(default): $(addprefix $(out_base)/,$(addsuffix /,$(build_examples))) + +# Install & Dist. +# +$(install) $(dist) $(dist-win): path := $(subst $(src_root)/,,$(src_base)) + +$(install): $(addprefix $(out_base)/,$(addsuffix /.install,$(all_examples))) + $(call install-data,$(src_base)/README,$(install_doc_dir)/xsd/$(path)/README) + +$(dist): $(addprefix $(out_base)/,$(addsuffix /.dist,$(all_examples))) + $(call install-data,$(src_base)/README,$(dist_prefix)/$(path)/README) + +$(dist-win): $(addprefix $(out_base)/,$(addsuffix /.dist-win,$(all_examples))) + $(call install-data,$(src_base)/README,$(dist_prefix)/$(path)/README.txt) + $(call message,,unix2dos $(dist_prefix)/$(path)/README.txt) + +# Clean. +# +$(clean): $(addprefix $(out_base)/,$(addsuffix /.clean,$(build_examples))) + +$(call include,$(bld_root)/install.make) + +ifneq ($(filter $(MAKECMDGOALS),dist dist-win install),) +$(foreach e,$(all_examples),$(call import,$(src_base)/$e/makefile)) +else +$(foreach e,$(build_examples),$(call import,$(src_base)/$e/makefile)) +endif diff --git a/examples/cxx/tree/order/mixed/README b/examples/cxx/tree/order/mixed/README new file mode 100644 index 0000000..e66c1ad --- /dev/null +++ b/examples/cxx/tree/order/mixed/README @@ -0,0 +1,45 @@ +This example shows how to use ordered types to capture mixed content +text and to maintain order information between elements and text. + +In this example we use mixed content model to describe text with +embedded links in the form: + + This paragraph talks about time. + +The example transforms such text into plain text with references in +the form: + + This paragraph talks about time[uri]. + +It also saves the modified text back to XML in order to verify the +element and text order. + +The example consists of the following files: + +text.xsd + XML Schema which describes "text with links" instance documents. + +text.xml + Sample XML instance document. + +text.hxx +text.cxx + C++ types that represent the given vocabulary as well as a set of + parsing and serialization functions. These are generated by XSD + from text.xsd. Note that the --ordered-type-mixed option is used + to indicate to the XSD compiler that all types with mixed content + should be automatically treated as ordered. + +driver.cxx + Driver for the example. It first calls one of the parsing functions + that constructs the object model from the input XML file. It then + iterates over the text and elements in the content order to convert + the document to its plain text representation. The driver then adds + another paragraph of text and a link to the object model while showing + how to maintain the content order. Finally, it saves the modified + text back to XML to verify that the content order is preserved in + the output document. + +To run the example on the sample XML instance document simply execute: + +$ ./driver text.xml diff --git a/examples/cxx/tree/order/mixed/driver.cxx b/examples/cxx/tree/order/mixed/driver.cxx new file mode 100644 index 0000000..9606b67 --- /dev/null +++ b/examples/cxx/tree/order/mixed/driver.cxx @@ -0,0 +1,89 @@ +// file : examples/cxx/tree/order/mixed/driver.cxx +// copyright : not copyrighted - public domain + +#include // std::auto_ptr +#include +#include + +#include "text.hxx" + +using std::cerr; +using std::endl; + +int +main (int argc, char* argv[]) +{ + if (argc != 2) + { + cerr << "usage: " << argv[0] << " text.xml" << endl; + return 1; + } + + try + { + std::auto_ptr t (text_ (argv[1])); + + // Print what we've got in content order. + // + for (text::content_order_const_iterator i (t->content_order ().begin ()); + i != t->content_order ().end (); + ++i) + { + switch (i->id) + { + case text::a_id: + { + const anchor& a (t->a ()[i->index]); + cerr << a << "[" << a.href () << "]"; + break; + } + case text::text_content_id: + { + const xml_schema::string& s (t->text_content ()[i->index]); + cerr << s; + break; + } + default: + { + assert (false); // Unknown content id. + } + } + } + + cerr << endl; + + // Modify the document. Note that we have to update both the content + // itself and content order sequences. + // + typedef text::content_order_type order_type; + + text::content_order_sequence& co (t->content_order ()); + text::text_content_sequence& tc (t->text_content ()); + + tc.push_back ("The last paragraph doesn't talk about "); + co.push_back (order_type (text::text_content_id, tc.size () - 1)); + + t->a ().push_back (anchor ("anything", "http://en.wikipedia.org")); + co.push_back (order_type (text::a_id, t->a ().size () - 1)); + + tc.push_back (" in particular.\n\n"); + co.push_back (order_type (text::text_content_id, tc.size () - 1)); + + // Serialize the modified document back to XML. + // + xml_schema::namespace_infomap map; + + map[""].schema = "text.xsd"; + + text_ (std::cout, + *t, + map, + "UTF-8", + xml_schema::flags::dont_pretty_print); + } + catch (const xml_schema::exception& e) + { + cerr << e << endl; + return 1; + } +} diff --git a/examples/cxx/tree/order/mixed/makefile b/examples/cxx/tree/order/mixed/makefile new file mode 100644 index 0000000..3e3eb30 --- /dev/null +++ b/examples/cxx/tree/order/mixed/makefile @@ -0,0 +1,99 @@ +# file : examples/cxx/tree/order/mixed/makefile +# copyright : Copyright (c) 2005-2014 Code Synthesis Tools CC +# license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +include $(dir $(lastword $(MAKEFILE_LIST)))../../../../../build/bootstrap.make + +xsd := text.xsd +cxx := driver.cxx + +obj := $(addprefix $(out_base)/,$(cxx:.cxx=.o) $(xsd:.xsd=.o)) +dep := $(obj:.o=.o.d) + +driver := $(out_base)/driver +install := $(out_base)/.install +dist := $(out_base)/.dist +dist-win := $(out_base)/.dist-win +clean := $(out_base)/.clean + +# Import. +# +$(call import,\ + $(scf_root)/import/libxerces-c/stub.make,\ + l: xerces_c.l,cpp-options: xerces_c.l.cpp-options) + +# Build. +# +$(driver): $(obj) $(xerces_c.l) -lnsl + +$(obj) $(dep): cpp_options := -I$(out_base) -I$(src_base) -I$(src_root)/libxsd +$(obj) $(dep): $(xerces_c.l.cpp-options) + +genf := $(xsd:.xsd=.hxx) $(xsd:.xsd=.ixx) $(xsd:.xsd=.cxx) +gen := $(addprefix $(out_base)/,$(genf)) + +$(gen): xsd := $(out_root)/xsd/xsd +$(gen): xsd_options += --generate-serialization --ordered-type-mixed +$(gen): $(out_root)/xsd/xsd + +$(call include-dep,$(dep),$(obj),$(gen)) + +# Convenience alias for default target. +# +$(out_base)/: $(driver) + + +# Install & Dist. +# +dist-common := $(out_base)/.dist-common + +$(install) $(dist) $(dist-win) $(dist-common): path := $(subst $(src_root)/,,$(src_base)) + +$(install): + $(call install-data,$(src_base)/README,$(install_doc_dir)/xsd/$(path)/README) + $(call install-data,$(src_base)/driver.cxx,$(install_doc_dir)/xsd/$(path)/driver.cxx) + $(call install-data,$(src_base)/text.xsd,$(install_doc_dir)/xsd/$(path)/text.xsd) + $(call install-data,$(src_base)/text.xml,$(install_doc_dir)/xsd/$(path)/text.xml) + +$(dist-common): + $(call install-data,$(src_base)/driver.cxx,$(dist_prefix)/$(path)/driver.cxx) + $(call install-data,$(src_base)/text.xsd,$(dist_prefix)/$(path)/text.xsd) + $(call install-data,$(src_base)/text.xml,$(dist_prefix)/$(path)/text.xml) + +$(dist): $(dist-common) + $(call install-data,$(src_base)/README,$(dist_prefix)/$(path)/README) + +$(dist-win): $(dist-common) + $(call install-data,$(src_base)/README,$(dist_prefix)/$(path)/README.txt) + $(call message,,unix2dos $(dist_prefix)/$(path)/README.txt) + +# Clean. +# +$(clean): $(driver).o.clean \ + $(addsuffix .cxx.clean,$(obj)) \ + $(addsuffix .cxx.clean,$(dep)) \ + $(addprefix $(out_base)/,$(xsd:.xsd=.cxx.xsd.clean)) + +# Generated .gitignore. +# +ifeq ($(out_base),$(src_base)) +$(gen): | $(out_base)/.gitignore +$(driver): | $(out_base)/.gitignore + +$(out_base)/.gitignore: files := driver $(genf) +$(clean): $(out_base)/.gitignore.clean + +$(call include,$(bld_root)/git/gitignore.make) +endif + +# How to. +# +$(call include,$(bld_root)/cxx/o-e.make) +$(call include,$(bld_root)/cxx/cxx-o.make) +$(call include,$(bld_root)/cxx/cxx-d.make) +$(call include,$(bld_root)/install.make) +$(call include,$(scf_root)/xsd/tree/xsd-cxx.make) + +# Dependencies. +# +$(call import,$(src_root)/xsd/makefile) diff --git a/examples/cxx/tree/order/mixed/text.xml b/examples/cxx/tree/order/mixed/text.xml new file mode 100644 index 0000000..815b76e --- /dev/null +++ b/examples/cxx/tree/order/mixed/text.xml @@ -0,0 +1,18 @@ + + + + + + +The first paragraph of this text talks about time. + +And this paragraph talks about space. + + diff --git a/examples/cxx/tree/order/mixed/text.xsd b/examples/cxx/tree/order/mixed/text.xsd new file mode 100644 index 0000000..bfb868e --- /dev/null +++ b/examples/cxx/tree/order/mixed/text.xsd @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libxsd/xsd/cxx/tree/elements.hxx b/libxsd/xsd/cxx/tree/elements.hxx index 6270c40..89be61e 100644 --- a/libxsd/xsd/cxx/tree/elements.hxx +++ b/libxsd/xsd/cxx/tree/elements.hxx @@ -21,6 +21,7 @@ #include #include #include // std::auto_ptr/unique_ptr +#include // std::size_t #include #include #include @@ -190,7 +191,6 @@ namespace xsd unsigned long x_; }; - // Parsing properties. Refer to xsd/cxx/xml/elements.hxx for XML- // related properties. // @@ -199,6 +199,44 @@ namespace xsd { }; + /** + * @brief Content order sequence entry. + * + * @nosubgrouping + */ + struct content_order + { + /** + * @brief Initialize an instance with passed id and index. + * + * @param id Content id. + * @param index Content index in the corresponding sequence. + */ + content_order (std::size_t id, std::size_t index = 0) + : id (id), index (index) + { + } + + /** + * @brief Content id. + */ + std::size_t id; + + /** + * @brief Content index. + */ + std::size_t index; + }; + + bool + operator== (const content_order&, const content_order&); + + bool + operator!= (const content_order&, const content_order&); + + bool + operator< (const content_order&, const content_order&); + //@cond // DOM user data keys. diff --git a/libxsd/xsd/cxx/tree/elements.ixx b/libxsd/xsd/cxx/tree/elements.ixx index 1098299..da8a83e 100644 --- a/libxsd/xsd/cxx/tree/elements.ixx +++ b/libxsd/xsd/cxx/tree/elements.ixx @@ -8,6 +8,27 @@ namespace xsd { namespace tree { + // content_order_type + // + + inline bool + operator== (const content_order& x, const content_order& y) + { + return x.id == y.id && x.index == y.index; + } + + inline bool + operator!= (const content_order& x, const content_order& y) + { + return !(x == y); + } + + inline bool + operator< (const content_order& x, const content_order& y) + { + return x.id < y.id || (x.id == y.id && x.index < y.index); + } + // type // diff --git a/libxsd/xsd/cxx/xml/dom/parsing-source.hxx b/libxsd/xsd/cxx/xml/dom/parsing-source.hxx index 1c53928..fac1006 100644 --- a/libxsd/xsd/cxx/xml/dom/parsing-source.hxx +++ b/libxsd/xsd/cxx/xml/dom/parsing-source.hxx @@ -30,30 +30,47 @@ namespace xsd { namespace dom { - // Parser state object. Can be used for parsing element, attributes, - // or both. + // Parser state object. Can be used for parsing elements (and + // optionally text), attributes, or both. // template class parser { public: - parser (const xercesc::DOMElement& e, bool ep, bool ap); + parser (const xercesc::DOMElement& e, bool ep, bool tp, bool ap); + // Content parsing. + // bool - more_elements () + more_content () { - return next_element_ != 0; + return next_content_ != 0; } const xercesc::DOMElement& cur_element () { - return *static_cast (next_element_); + return *static_cast (next_content_); + } + + const xercesc::DOMText& + cur_text () + { + return *static_cast (next_content_); + } + + bool + cur_is_text () + { + return next_content_->getNodeType () != + xercesc::DOMNode::ELEMENT_NODE; } void - next_element (); + next_content (bool text); + // Attribute parsing. + // bool more_attributes () { @@ -86,7 +103,7 @@ namespace xsd private: const xercesc::DOMElement& element_; - const xercesc::DOMNode* next_element_; + const xercesc::DOMNode* next_content_; const xercesc::DOMNamedNodeMap* a_; XMLSize_t ai_; // Index of the next DOMAttr. diff --git a/libxsd/xsd/cxx/xml/dom/parsing-source.txx b/libxsd/xsd/cxx/xml/dom/parsing-source.txx index 8eb4d6e..c789fa6 100644 --- a/libxsd/xsd/cxx/xml/dom/parsing-source.txx +++ b/libxsd/xsd/cxx/xml/dom/parsing-source.txx @@ -29,9 +29,9 @@ namespace xsd // template parser:: - parser (const xercesc::DOMElement& e, bool ep, bool ap) + parser (const xercesc::DOMElement& e, bool ep, bool tp, bool ap) : element_ (e), - next_element_ (0), + next_content_ (0), a_ (0), ai_ (0) { @@ -39,10 +39,21 @@ namespace xsd if (ep) { - for (next_element_ = e.getFirstChild (); - next_element_ != 0 && - next_element_->getNodeType () != DOMNode::ELEMENT_NODE; - next_element_ = next_element_->getNextSibling ()) /*noop*/; + for (next_content_ = e.getFirstChild ();; + next_content_ = next_content_->getNextSibling ()) + { + if (next_content_ == 0) + break; + + DOMNode::NodeType t (next_content_->getNodeType ()); + + if (t == DOMNode::ELEMENT_NODE) + break; + + if (tp && (t == DOMNode::TEXT_NODE || + t == DOMNode::CDATA_SECTION_NODE)) + break; + } } if (ap) @@ -54,14 +65,25 @@ namespace xsd template void parser:: - next_element () + next_content (bool tp) { using xercesc::DOMNode; - for (next_element_ = next_element_->getNextSibling (); - next_element_ != 0 && - next_element_->getNodeType () != DOMNode::ELEMENT_NODE; - next_element_ = next_element_->getNextSibling ())/*noop*/; + for (next_content_ = next_content_->getNextSibling ();; + next_content_ = next_content_->getNextSibling ()) + { + if (next_content_ == 0) + break; + + DOMNode::NodeType t (next_content_->getNodeType ()); + + if (t == DOMNode::ELEMENT_NODE) + break; + + if (tp && (t == DOMNode::TEXT_NODE || + t == DOMNode::CDATA_SECTION_NODE)) + break; + } } // parse() diff --git a/tests/cxx/tree/makefile b/tests/cxx/tree/makefile index 43f806b..cd081e8 100644 --- a/tests/cxx/tree/makefile +++ b/tests/cxx/tree/makefile @@ -20,6 +20,7 @@ float \ list \ name-clash \ naming \ +order \ polymorphism \ prefix \ test-template \ diff --git a/tests/cxx/tree/order/driver.cxx b/tests/cxx/tree/order/driver.cxx new file mode 100644 index 0000000..01d8d9f --- /dev/null +++ b/tests/cxx/tree/order/driver.cxx @@ -0,0 +1,65 @@ +// file : tests/cxx/tree/order/driver.cxx +// copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +// Test ordered type support. +// + +#include // std::auto_ptr/unique_ptr +#include +#include + +#include +#include + +#include "test.hxx" + +using namespace std; +using namespace test; +using namespace xercesc; + +int +main (int argc, char* argv[]) +{ + if (argc != 2) + { + cerr << "usage: " << argv[0] << " test.xml" << endl; + return 1; + } + + XMLPlatformUtils::Initialize (); + + try + { + XSD_AUTO_PTR r (root_ (argv[1], xml_schema::flags::dont_initialize)); + + root c (*r); + assert (c == *r); + + for (root::t1_const_iterator j (r->t1 ().begin ()); + j != r->t1 ().end (); ++j) + { + const t1_derived& d (*j); + + for (t1_derived::content_order_const_iterator i ( + d.content_order ().begin ()); i != d.content_order ().end (); ++i) + { + cout << i->id << ' ' << i->index << endl; + } + } + + xml_schema::namespace_infomap map; + + map["t"].name = "test"; + map["t1"].name = "test1"; + + root_ (cout, *r, map, "UTF-8", xml_schema::flags::dont_initialize); + } + catch (xml_schema::exception const& e) + { + cerr << e << endl; + return 1; + } + + XMLPlatformUtils::Terminate (); +} diff --git a/tests/cxx/tree/order/makefile b/tests/cxx/tree/order/makefile new file mode 100644 index 0000000..0dff0f9 --- /dev/null +++ b/tests/cxx/tree/order/makefile @@ -0,0 +1,94 @@ +# file : tests/cxx/tree/order/makefile +# copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +# license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +include $(dir $(lastword $(MAKEFILE_LIST)))../../../../build/bootstrap.make + +xsd := test.xsd +cxx := driver.cxx + +obj := $(addprefix $(out_base)/,$(cxx:.cxx=.o) $(xsd:.xsd=.o)) +dep := $(obj:.o=.o.d) + +driver := $(out_base)/driver +test := $(out_base)/.test +clean := $(out_base)/.clean + + +# Import. +# +$(call import,\ + $(scf_root)/import/libxerces-c/stub.make,\ + l: xerces_c.l,cpp-options: xerces_c.l.cpp-options) + + +# Build. +# +$(driver): $(obj) $(xerces_c.l) + +$(obj) $(dep): cpp_options := -I$(out_base) -I$(src_base) -I$(src_root)/libxsd +$(obj) $(dep): $(xerces_c.l.cpp-options) + +genf := $(xsd:.xsd=.hxx) $(xsd:.xsd=.ixx) $(xsd:.xsd=.cxx) +gen := $(addprefix $(out_base)/,$(genf)) + +$(gen): xsd := $(out_root)/xsd/xsd +$(gen): xsd_options += --generate-serialization --generate-wildcard \ +--generate-comparison \ +--ordered-type t1_base --ordered-type t1_derived \ +--ordered-type t2_base --ordered-type t2_derived \ +--ordered-type t3_type \ +--ordered-type t4_base --ordered-type t4_derived \ +--ordered-type t5_base --ordered-type t5_derived \ +--ordered-type t6_base --ordered-type t6_derived \ +--ordered-type t7_type +$(gen): $(out_root)/xsd/xsd + +$(call include-dep,$(dep),$(obj),$(gen)) + +# Convenience alias for default target. +# +$(out_base)/: $(driver) + + +# Test. +# +$(test): driver := $(driver) +$(test): $(driver) $(src_base)/test.xml $(src_base)/output + $(call message,test $$1,$$1 $(src_base)/test.xml | diff -u $(src_base)/output -,$(driver)) + +# Clean. +# +$(clean): $(driver).o.clean \ + $(addsuffix .cxx.clean,$(obj)) \ + $(addsuffix .cxx.clean,$(dep)) \ + $(addprefix $(out_base)/,$(xsd:.xsd=.cxx.xsd.clean)) + +# Generated .gitignore. +# +ifeq ($(out_base),$(src_base)) +$(gen): | $(out_base)/.gitignore +$(driver): | $(out_base)/.gitignore + +$(out_base)/.gitignore: files := driver $(genf) +$(clean): $(out_base)/.gitignore.clean + +$(call include,$(bld_root)/git/gitignore.make) +endif + +# How to. +# +$(call include,$(bld_root)/cxx/o-e.make) +$(call include,$(bld_root)/cxx/cxx-o.make) +$(call include,$(bld_root)/cxx/cxx-d.make) + +$(call include,$(bld_root)/cxx/standard.make) # cxx_standard +ifdef cxx_standard +$(gen): xsd_options += --std $(cxx_standard) +$(call include,$(scf_root)/xsd/tree/xsd-cxx.make) +endif + + +# Dependencies. +# +$(call import,$(src_root)/xsd/makefile) diff --git a/tests/cxx/tree/order/output b/tests/cxx/tree/order/output new file mode 100644 index 0000000..73442fe --- /dev/null +++ b/tests/cxx/tree/order/output @@ -0,0 +1,92 @@ +2 0 +1 0 +1 1 +2 1 +3 0 +4 0 +3 1 +4 1 + + + + b1 + a1 + a2 + b2 + c1 + d1 + e1 + d2 + f1 + + + b1 + a1 + b2 + a2 + + + a1 + b1 + c1 + c2 + + + a1 + c1 + + + t1 + + b1 + t2 + + a1 + t3 + + b2 + t4 + + a2 + t5 + + d1 + t6 + + c1 + t7 + + + + t5a + + + t1 + + b1 + t2 + + a1 + t3 + + a2 + t4 + + b2 + t5 + + + + t6 + + + t1 + + a1 + t2 + + b1 + t3 + + + diff --git a/tests/cxx/tree/order/test.xml b/tests/cxx/tree/order/test.xml new file mode 100644 index 0000000..cd82936 --- /dev/null +++ b/tests/cxx/tree/order/test.xml @@ -0,0 +1,71 @@ + + + b1 + a1 + a2 + b2 + c1 + d1 + e1 + d2 + f1 + + + b1 + a1 + b2 + a2 + + + a1 + b1 + c1 + c2 + + + a1 + c1 + + + t1 + b1 + t2 + a1 + t3 + b2 + t4 + a2 + t5 + d1 + t6 + c1 + t7 + + + t5a + + + t1 + b1 + t2 + a1 + t3 + a2 + t4 + b2 + t5 + + + t6 + + + t1 + a1 + t2 + b1 + t3 + + diff --git a/tests/cxx/tree/order/test.xsd b/tests/cxx/tree/order/test.xsd new file mode 100644 index 0000000..c30c027 --- /dev/null +++ b/tests/cxx/tree/order/test.xsd @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/cxx/tree/wildcard/driver.cxx b/tests/cxx/tree/wildcard/driver.cxx index 9d0945f..e2db827 100644 --- a/tests/cxx/tree/wildcard/driver.cxx +++ b/tests/cxx/tree/wildcard/driver.cxx @@ -9,6 +9,9 @@ #include #include +#include +#include + #include "test.hxx" // Get XSD_CXX11 defined. #include diff --git a/xsd/cxx/tree/elements.hxx b/xsd/cxx/tree/elements.hxx index f3f9f66..6874e6a 100644 --- a/xsd/cxx/tree/elements.hxx +++ b/xsd/cxx/tree/elements.hxx @@ -6,6 +6,7 @@ #define CXX_TREE_ELEMENTS_HXX #include +#include #include #include #include @@ -68,6 +69,54 @@ namespace CXX String reason_; }; + // A set of potentially qualified XML Schema type names. + // + struct TypeNameSet + { + template + TypeNameSet (I begin, I end) + { + for (; begin != end; ++begin) + insert (*begin); + } + + void + insert (String const& name) + { + size_t p (name.rfind ('#')); + + if (p == String::npos) + unames_.insert (name); + else + qnames_.insert (name); + } + + bool + find (SemanticGraph::Type& t) + { + if (!unames_.empty ()) + { + if (unames_.find (t.name ()) != unames_.end ()) + return true; + } + + if (!qnames_.empty ()) + { + if (qnames_.find (t.scope ().name () + L"#" + t.name ()) != + qnames_.end ()) + return true; + } + + return false; + } + + private: + typedef std::set StringSet; + + StringSet unames_; + StringSet qnames_; + }; + // // class Context: public CXX::Context @@ -157,6 +206,22 @@ namespace CXX // // public: + static bool + ordered_p (SemanticGraph::Type const& t) + { + return t.context ().count ("ordered") && + t.context ().get ("ordered"); + } + + // Check if we are generating mixed support for this type. We only + // do it for ordered types. + // + static bool + mixed_p (SemanticGraph::Complex const& c) + { + return c.mixed_p () && ordered_p (c); + } + bool polymorphic_p (SemanticGraph::Type&); @@ -1338,7 +1403,11 @@ namespace CXX virtual void traverse (SemanticGraph::Complex& c) { - names (c, names_); + if (c.mixed_p ()) + v_ = false; + + if (v_) + names (c, names_); if (v_) inherits (c, inherits_); diff --git a/xsd/cxx/tree/fundamental-header.hxx b/xsd/cxx/tree/fundamental-header.hxx index dbe7c9a..1145c0c 100644 --- a/xsd/cxx/tree/fundamental-header.hxx +++ b/xsd/cxx/tree/fundamental-header.hxx @@ -724,6 +724,17 @@ namespace CXX bool serialization (options.generate_serialization ()); bool element_map (options.generate_element_map ()); + { + if (doxygen) + os << endl + << "/**" << endl + << " * @brief Content order sequence entry." << endl + << " */" << endl; + + gen_typedef (c.get ("content-order"), + "::xsd::cxx::tree::content_order"); + } + if (options.generate_element_type ()) { if (doxygen) diff --git a/xsd/cxx/tree/generator.cxx b/xsd/cxx/tree/generator.cxx index 5178bb1..f7eb1fa 100644 --- a/xsd/cxx/tree/generator.cxx +++ b/xsd/cxx/tree/generator.cxx @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -234,6 +235,15 @@ namespace CXX bool gen_cxx (!ops.generate_dep_only ()); + // Process ordered types. + // + if (gen_cxx) + { + OrderProcessor proc; + if (!proc.process (ops, schema, file_path)) + throw Failed (); + } + // Process names. // if (gen_cxx) diff --git a/xsd/cxx/tree/name-processor.cxx b/xsd/cxx/tree/name-processor.cxx index e34ae84..1f3844b 100644 --- a/xsd/cxx/tree/name-processor.cxx +++ b/xsd/cxx/tree/name-processor.cxx @@ -60,6 +60,7 @@ namespace CXX seq_modifier_regex (seq_modifier_regex_), parser_regex (parser_regex_), serializer_regex (serializer_regex_), + const_regex (const_regex_), enumerator_regex (enumerator_regex_), element_type_regex (element_type_regex_) { @@ -231,6 +232,30 @@ namespace CXX "serializer"); } + // Const regex. + // + { + if (fn == "knr") + { + const_regex.push_back ("/([^,]+),([^,]+),([^,]+)/$1_$2_$3/"); + const_regex.push_back ("/([^,]+),([^,]+)/$1_$2/"); + } + else if (fn == "lcc") + { + const_regex.push_back ("/([^,]+),([^,]+),([^,]+)/\\l$1_\\u$2_\\u$3/"); + const_regex.push_back ("/([^,]+),([^,]+)/\\l$1\\u$2/"); + } + else + { + // Java: all uppercase. + // + const_regex.push_back ("/([^,]+),([^,]+),([^,]+)/\\U$1_$2_$3/"); + const_regex.push_back ("/([^,]+),([^,]+)/\\U$1_$2/"); + } + + compile_regex (options.const_regex (), const_regex, "const"); + } + // Enumerator name regex. // { @@ -267,6 +292,7 @@ namespace CXX seq_modifier_regex (c.seq_modifier_regex), parser_regex (c.parser_regex), serializer_regex (c.serializer_regex), + const_regex (c.const_regex), enumerator_regex (c.enumerator_regex), element_type_regex (c.element_type_regex) { @@ -522,6 +548,7 @@ namespace CXX RegexVector seq_modifier_regex_; RegexVector parser_regex_; RegexVector serializer_regex_; + RegexVector const_regex_; RegexVector enumerator_regex_; RegexVector element_type_regex_; @@ -542,6 +569,7 @@ namespace CXX RegexVector& seq_modifier_regex; RegexVector& parser_regex; RegexVector& serializer_regex; + RegexVector& const_regex; RegexVector& enumerator_regex; RegexVector& element_type_regex; }; @@ -655,6 +683,9 @@ namespace CXX if (Tree::Context::skip (m)) return; + SemanticGraph::Complex& c ( + dynamic_cast (m.scope ())); + size_t max (Tree::Context::max (m)); size_t min (Tree::Context::min (m)); @@ -662,7 +693,7 @@ namespace CXX String const& b (m.context ().get ("name")); bool def_attr (m.default_p () && - m.is_a ()); + m.is_a ()); // Accessors/modifiers. Note that we postpone inserting // the names into the name_set to avoid over-escaping. @@ -816,7 +847,7 @@ namespace CXX process_regex ( s + L",default,value", accessor_regex, L"accessor"))); - m.context ().set ( "default-value", find_name (an, name_set_)); + m.context ().set ("default-value", find_name (an, name_set_)); bool lit (false); { @@ -832,6 +863,18 @@ namespace CXX } } } + + // Element id. + // + if (m.is_a () && ordered_p (c)) + { + String id ( + escape ( + process_regex ( + s + L",id", const_regex, L"const"))); + + m.context ().set ("ordered-id-name", find_name (id, name_set_)); + } } private: @@ -857,6 +900,9 @@ namespace CXX virtual void traverse (SemanticGraph::Any& a) { + SemanticGraph::Complex& c ( + dynamic_cast (a.scope ())); + size_t max (Tree::Context::max (a)); size_t min (Tree::Context::min (a)); @@ -972,6 +1018,18 @@ namespace CXX // a.context ().set ("member", find_name (b + L"_", name_set_)); + // Wildcard id. + // + if (ordered_p (c)) + { + String id ( + escape ( + process_regex ( + s + L",id", const_regex, L"const"))); + + a.context ().set ("ordered-id-name", find_name (id, name_set_)); + } + if (!has_wildcard_) has_wildcard_ = true; } @@ -1057,17 +1115,17 @@ namespace CXX virtual void traverse (Type& c) { - SemanticGraph::Context& cc (c.context ()); + SemanticGraph::Context& ctx (c.context ()); // We leave this set around to allow other mappings to use // this information. // - cc.set ("cxx-tree-name-processor-stem-set", NameSet ()); - cc.set ("cxx-tree-name-processor-member-set", NameSet ()); + ctx.set ("cxx-tree-name-processor-stem-set", NameSet ()); + ctx.set ("cxx-tree-name-processor-member-set", NameSet ()); // Use processed name. // - String name (cc.get ("name")); + String name (ctx.get ("name")); // If renamed name is empty then we are not generating // anything for this type and name processing is not @@ -1077,10 +1135,10 @@ namespace CXX return; NameSet& stem_set ( - cc.get ("cxx-tree-name-processor-stem-set")); + ctx.get ("cxx-tree-name-processor-stem-set")); NameSet& member_set ( - cc.get ("cxx-tree-name-processor-member-set")); + ctx.get ("cxx-tree-name-processor-member-set")); stem_set.insert (c.name ()); member_set.insert (name); @@ -1136,6 +1194,116 @@ namespace CXX Complex::names (c, names); } + // Names for the mixed content. + // + if (mixed_p (c)) + { + // Check if we already have the mixed content down inheritance + // hierarchy. + // + using SemanticGraph::Complex; + + for (Complex* p (&c); p->inherits_p ();) + { + if (Complex* b = dynamic_cast ( + &p->inherits ().base ())) + { + if (mixed_p (*b)) + { + SemanticGraph::Context& bctx (b->context ()); + ctx.set ("mixed-type", bctx.get ("mixed-type")); + ctx.set ("mixed-const-iterator", + bctx.get ("mixed-const-iterator")); + ctx.set ("mixed-ordered-id-name", + bctx.get ("mixed-ordered-id-name")); + ctx.set ("mixed-aname", bctx.get ("mixed-aname")); + ctx.set ("mixed-member", bctx.get ("mixed-member")); + ctx.set ("mixed-in-base", true); + break; + } + + p = b; + } + else + break; + } + + // If not, set up the names. + // + if (!ctx.count ("mixed-in-base")) + { + String s (find_name (L"text,content", stem_set)); + String n (find_name (escape (s), member_set, false)); + + String an (find_name ( + escape (process_regex (s, + seq_accessor_regex, + accessor_regex, + L"sequence accessor")), + member_set, + false)); + + String mn (find_name ( + escape (process_regex (s, + seq_modifier_regex, + modifier_regex, + L"sequence modifier")), + member_set, + false)); + + ctx.set ("mixed-aname", an); + ctx.set ("mixed-mname", mn); + + member_set.insert (name); + + if (an != n) + member_set.insert (an); + + if (mn != n && mn != an) + member_set.insert (mn); + + // Types. + // + ctx.set ( + "mixed-type", + find_name ( + escape (process_regex (s + L",type", type_regex, L"type")), + member_set)); + + ctx.set ( + "mixed-container", + find_name ( + escape (process_regex (s + L",sequence", type_regex, L"type")), + member_set)); + + ctx.set ( + "mixed-iterator", + find_name ( + escape (process_regex (s + L",iterator", type_regex, L"type")), + member_set)); + + ctx.set ( + "mixed-const-iterator", + find_name ( + escape ( + process_regex (s + L",const,iterator", type_regex, L"type")), + member_set)); + + // Text content id. + // + ctx.set ( + "mixed-ordered-id-name", + find_name ( + escape ( + process_regex (s + L",id", const_regex, L"const")), + member_set)); + + // Data member. + // + ctx.set ("mixed-member", find_name (n + L"_", member_set)); + } + } + // Names for wildcards. // if (options.generate_wildcard ()) @@ -1190,6 +1358,105 @@ namespace CXX } } } + + // Names for the order container. + // + if (ordered_p (c)) + { + // Check if we already have the order container down + // inheritance hierarchy. + // + using SemanticGraph::Complex; + + for (Complex* p (&c); p->inherits_p ();) + { + if (Complex* b = dynamic_cast ( + &p->inherits ().base ())) + { + if (ordered_p (*b)) + { + SemanticGraph::Context& bctx (b->context ()); + ctx.set ("order-type", bctx.get ("order-type")); + ctx.set ("order-const-iterator", + bctx.get ("order-const-iterator")); + ctx.set ("order-aname", bctx.get ("order-aname")); + ctx.set ("order-member", bctx.get ("order-member")); + ctx.set ("order-in-base", true); + break; + } + + p = b; + } + else + break; + } + + // If not, set up the names. + // + if (!ctx.count ("order-in-base")) + { + String s (find_name (L"content,order", stem_set)); + String n (find_name (escape (s), member_set, false)); + + String an (find_name ( + escape (process_regex (s, + seq_accessor_regex, + accessor_regex, + L"sequence accessor")), + member_set, + false)); + + String mn (find_name ( + escape (process_regex (s, + seq_modifier_regex, + modifier_regex, + L"sequence modifier")), + member_set, + false)); + + ctx.set ("order-aname", an); + ctx.set ("order-mname", mn); + + member_set.insert (name); + + if (an != n) + member_set.insert (an); + + if (mn != n && mn != an) + member_set.insert (mn); + + // Types. + // + ctx.set ( + "order-type", + find_name ( + escape (process_regex (s + L",type", type_regex, L"type")), + member_set)); + + ctx.set ( + "order-container", + find_name ( + escape (process_regex (s + L",sequence", type_regex, L"type")), + member_set)); + + ctx.set ( + "order-iterator", + find_name ( + escape (process_regex (s + L",iterator", type_regex, L"type")), + member_set)); + + ctx.set ( + "order-const-iterator", + find_name ( + escape ( + process_regex (s + L",const,iterator", type_regex, L"type")), + member_set)); + + // Data member. + // + ctx.set ("order-member", find_name (n + L"_", member_set)); + } + } } }; @@ -1870,6 +2137,8 @@ namespace CXX process_name (n, "buffer", "buffer"); process_name (n, "time,zone", "time-zone"); + process_name (n, "content,order", "content-order"); + if (options.generate_element_type ()) process_name (n, "element,type", "element-type"); diff --git a/xsd/cxx/tree/options.cli b/xsd/cxx/tree/options.cli index ac126fe..026e4a1 100644 --- a/xsd/cxx/tree/options.cli +++ b/xsd/cxx/tree/options.cli @@ -51,6 +51,58 @@ namespace CXX schemas that define the same polymorphic types." }; + // Ordered content. + // + NarrowStrings --ordered-type + { + "", + "Indicate that element order in is significant. An example + would be a complex type with unbounded choice as a content model + where the element order in XML has application-specific semantics. + For ordered types the compiler generates a special container data + member and a corresponding set of accessors and modifiers that are + used to capture the order of elements and, for mixed content, of + text. + + The argument is an XML Schema type name that can be optionally + qualified with a namespace in the \c{\i{namespace}\b{#}\i{name}} form. + Note also that you will need to specify this option when compiling + every schema file that has other ordered types derived from this + type." + }; + + bool --ordered-type-derived + { + "Automatically treat types derived from ordered bases as also + ordered. This is primarily useful if you would like to be able + to iterate over the complete content using the content order + container." + }; + + bool --ordered-type-mixed + { + "Automatically treat complex types with mixed content as ordered." + }; + + bool --ordered-type-all + { + "Indicate that element order in all types is significant." + }; + + NarrowString --order-container + { + "", + "Specify a custom class template that should be used as a container + for the content order in ordered types instead of the default + \cb{std::vector}. See \cb{--ordered-type} for more information on + ordered type. This option is primarily useful if you need to + perform more complex lookups in the content order container, for + example by element id. In this case, a container like Boost + multi-index may be more convenient. Note that if using a custom + container, you will also most likely need to include the relevant + headers using the \cb{--hxx-prologue*} options." + }; + // Features. // bool --generate-serialization @@ -299,6 +351,14 @@ namespace CXX the NAMING CONVENTION section below for more information." }; + NarrowStrings --const-regex + { + "", + "Add to the list of regular expressions used to translate + XML Schema-derived names to C++ constant names. See the NAMING + CONVENTION section below for more information." + }; + NarrowStrings --enumerator-regex { "", diff --git a/xsd/cxx/tree/order-processor.cxx b/xsd/cxx/tree/order-processor.cxx new file mode 100644 index 0000000..68eee8c --- /dev/null +++ b/xsd/cxx/tree/order-processor.cxx @@ -0,0 +1,244 @@ +// file : xsde/cxx/tree/order-processor.cxx +// copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#include + +#include +#include + +#include +#include + +using namespace std; + +namespace CXX +{ + namespace Tree + { + namespace + { + struct Member: Traversal::Element, Traversal::Any + { + Member (size_t count): count_ (count) {} + + virtual void + traverse (SemanticGraph::Element& e) + { + if (Context::skip (e)) + return; + + e.context ().set ("ordered-id", count_++); + } + + virtual void + traverse (SemanticGraph::Any& a) + { + a.context ().set ("ordered-id", count_++); + } + + size_t count_; + }; + + // + // + struct Type: Traversal::Complex + { + Type (TypeNameSet& ordered_types, bool derived, bool mixed, bool all) + : ordered_types_ (ordered_types), + derived_ (derived), + mixed_ (mixed), + all_ (all) + { + } + + virtual void + traverse (SemanticGraph::Complex& c) + { + SemanticGraph::Context& ctx (c.context ()); + + if (!ctx.count ("ordered")) + { + // First process our base. + // + if (c.inherits_p ()) + { + SemanticGraph::Type& b (c.inherits ().base ()); + + if (!b.context ().count ("ordered")) + dispatch (b); + } + + // See if our base (not necessarily immediate) is ordered. + // + using SemanticGraph::Complex; + + Complex* b (0); + + for (Complex* p (&c); p->inherits_p ();) + { + if ((b = dynamic_cast (&p->inherits ().base ()))) + { + if (Context::ordered_p (*b)) + break; + + p = b; + } + else + break; + } + + bool o (all_ || + (derived_ && b != 0 && Context::ordered_p (*b)) || + (mixed_ && c.mixed_p ()) || + ordered_types_.find (c)); + ctx.set ("ordered", o); + + // Assign ids to elements and wildcards, calculate total count. + // + if (o) + { + size_t count ( + b != 0 && Context::ordered_p (*b) + ? b->context ().get ("ordered-count") + : 1); + + ctx.set ("ordered-start", count); + + Member m (count); + Traversal::Names n (m); + names (c, n); + + // Assign content id for mixed text. + // + if (Context::mixed_p (c) && count == 1) + ctx.set ("mixed-ordered-id", m.count_++); + + ctx.set ("ordered-count", m.count_); + } + } + } + + private: + TypeNameSet& ordered_types_; + bool derived_; + bool mixed_; + bool all_; + }; + + // Go into sourced/included/imported schemas while making sure + // we don't process the same stuff more than once. + // + struct Uses: Traversal::Sources, + Traversal::Includes, + Traversal::Imports + { + Uses (char const* seen_key) + : seen_key_ (seen_key) + { + } + + virtual void + traverse (SemanticGraph::Sources& sr) + { + SemanticGraph::Schema& s (sr.schema ()); + + if (!s.context ().count (seen_key_)) + { + s.context ().set (seen_key_, true); + Traversal::Sources::traverse (sr); + } + } + + virtual void + traverse (SemanticGraph::Includes& i) + { + SemanticGraph::Schema& s (i.schema ()); + + if (!s.context ().count (seen_key_)) + { + s.context ().set (seen_key_, true); + Traversal::Includes::traverse (i); + } + } + + virtual void + traverse (SemanticGraph::Imports& i) + { + SemanticGraph::Schema& s (i.schema ()); + + if (!s.context ().count (seen_key_)) + { + s.context ().set (seen_key_, true); + Traversal::Imports::traverse (i); + } + } + + private: + char const* seen_key_; + }; + + char const* seen_key = "cxx-tree-order-processor-seen"; + + bool + process_impl (options const& ops, + SemanticGraph::Schema& tu, + SemanticGraph::Path const&) + { + // Prepare a set of ordered types. + // + TypeNameSet ordered_types (ops.ordered_type ().begin (), + ops.ordered_type ().end ()); + + // Root schema in the file-per-type mode is just a bunch + // of includes without a namespace. + // + SemanticGraph::Schema::NamesIterator i (tu.names_begin ()); + + // Nothing to do if this is the XML Schema namespace. + // + if (i == tu.names_end () || + i->named ().name () != L"http://www.w3.org/2001/XMLSchema") + { + // Note that we check first if this schema has already been + // processed which may happen in the file-per-type compilation + // mode. + // + if (!tu.context ().count (seen_key)) + { + Traversal::Schema schema; + Uses uses (seen_key); + + schema >> uses >> schema; + + Traversal::Names schema_names; + Traversal::Namespace ns; + Traversal::Names ns_names; + Type type (ordered_types, + ops.ordered_type_derived (), + ops.ordered_type_mixed (), + ops.ordered_type_all ()); + + schema >> schema_names >> ns >> ns_names >> type; + + // Some twisted schemas do recusive self-inclusion. + // + tu.context ().set (seen_key, true); + + schema.dispatch (tu); + } + } + + return true; + } + } + + bool OrderProcessor:: + process (options const& ops, + SemanticGraph::Schema& tu, + SemanticGraph::Path const& file) + { + return process_impl (ops, tu, file); + } + } +} diff --git a/xsd/cxx/tree/order-processor.hxx b/xsd/cxx/tree/order-processor.hxx new file mode 100644 index 0000000..fa68d21 --- /dev/null +++ b/xsd/cxx/tree/order-processor.hxx @@ -0,0 +1,30 @@ +// file : xsde/cxx/tree/order-processor.hxx +// copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC +// license : GNU GPL v2 + exceptions; see accompanying LICENSE file + +#ifndef CXX_TREE_ORDER_PROCESSOR_HXX +#define CXX_TREE_ORDER_PROCESSOR_HXX + +#include + +#include +#include + +#include + +namespace CXX +{ + namespace Tree + { + class OrderProcessor + { + public: + bool + process (options const&, + XSDFrontend::SemanticGraph::Schema&, + XSDFrontend::SemanticGraph::Path const& file); + }; + } +} + +#endif // CXX_TREE_ORDER_PROCESSOR_HXX diff --git a/xsd/cxx/tree/polymorphism-processor.cxx b/xsd/cxx/tree/polymorphism-processor.cxx index fb96ba7..cbd2471 100644 --- a/xsd/cxx/tree/polymorphism-processor.cxx +++ b/xsd/cxx/tree/polymorphism-processor.cxx @@ -2,7 +2,6 @@ // copyright : Copyright (c) 2006-2014 Code Synthesis Tools CC // license : GNU GPL v2 + exceptions; see accompanying LICENSE file -#include #include #include @@ -19,59 +18,12 @@ namespace CXX { namespace { - struct TypeSet - { - template - TypeSet (I begin, I end) - { - for (; begin != end; ++begin) - insert (*begin); - } - - void - insert (String const& name) - { - size_t p (name.rfind ('#')); - - if (p == String::npos) - unames_.insert (name); - else - qnames_.insert (name); - } - - bool - find (SemanticGraph::Type& t) - { - if (!unames_.empty ()) - { - if (unames_.find (t.name ()) != unames_.end ()) - return true; - } - - if (!qnames_.empty ()) - { - if (qnames_.find (t.scope ().name () + L"#" + t.name ()) != - qnames_.end ()) - return true; - } - - return false; - } - - private: - typedef set StringSet; - - StringSet unames_; - StringSet qnames_; - }; - - // // struct Type: Traversal::Type, Traversal::Complex { - Type (TypeSet& poly_types) + Type (TypeNameSet& poly_types) : poly_types_ (poly_types) { } @@ -110,7 +62,7 @@ namespace CXX } private: - TypeSet& poly_types_; + TypeNameSet& poly_types_; }; struct FundType: Traversal::AnyType, @@ -169,7 +121,7 @@ namespace CXX Traversal::Fundamental::Entity, Traversal::Fundamental::Entities { - FundType (TypeSet& poly_types, bool& valid) + FundType (TypeNameSet& poly_types, bool& valid) : poly_types_ (poly_types), valid_ (valid) { } @@ -493,13 +445,13 @@ namespace CXX } private: - TypeSet& poly_types_; + TypeNameSet& poly_types_; bool& valid_; }; struct GlobalElement: Traversal::Element { - GlobalElement (TypeSet& poly_types, + GlobalElement (TypeNameSet& poly_types, bool& valid, const WarningSet& disabled_warnings) : poly_types_ (poly_types), valid_ (valid), warning_ (true) @@ -603,7 +555,7 @@ namespace CXX } private: - TypeSet& poly_types_; + TypeNameSet& poly_types_; bool& valid_; bool warning_; }; @@ -674,8 +626,8 @@ namespace CXX // Prepare a set of polymorphic types. // - TypeSet poly_types (ops.polymorphic_type ().begin (), - ops.polymorphic_type ().end ()); + TypeNameSet poly_types (ops.polymorphic_type ().begin (), + ops.polymorphic_type ().end ()); // Root schema in the file-per-type mode is just a bunch // of includes without a namespace. diff --git a/xsd/cxx/tree/serialization-source.cxx b/xsd/cxx/tree/serialization-source.cxx index 8cb3463..559a3cf 100644 --- a/xsd/cxx/tree/serialization-source.cxx +++ b/xsd/cxx/tree/serialization-source.cxx @@ -295,6 +295,11 @@ namespace CXX if (skip (e)) return; + SemanticGraph::Complex& c ( + dynamic_cast (e.scope ())); + + bool ordered (ordered_p (c)); + String const& aname (eaname (e)); String ns (e.qualified_p () ? e.namespace_ ().name () : ""); String type (scope + L"::" + etype (e)); @@ -311,9 +316,14 @@ namespace CXX os << "// " << comment (e.name ()) << endl << "//" << endl; + if (ordered) + os << "case " << scope << "::" << + e.context ().get ("ordered-id-name") << ":" + << "{"; + if (poly) { - os << "{" + os << (ordered ? "" : "{") << "::xsd::cxx::tree::type_serializer_map< " << char_type << " >& tsm (" << endl << "::xsd::cxx::tree::type_serializer_map_instance< " << @@ -325,15 +335,22 @@ namespace CXX { // sequence // - os << "for (" << scope << "::" << econst_iterator (e) << endl - << "b (i." << aname << " ().begin ()), " << - "n (i." << aname << " ().end ());" << endl - << "b != n; ++b)" - << "{"; + if (ordered) + os << "const " << type << "& x (i." << aname << + " ()[b->index]);" + << endl; + else + os << "for (" << scope << "::" << econst_iterator (e) << endl + << "b (i." << aname << " ().begin ()), " << + "n (i." << aname << " ().end ());" << endl + << "b != n; ++b)" + << "{"; + + char const* x (ordered ? "x" : "*b"); if (poly) { - os << "if (typeid (" << type << ") == typeid (*b))" + os << "if (typeid (" << type << ") == typeid (" << x << "))" << "{" << xerces_ns << "::DOMElement& s (" << endl << "::xsd::cxx::xml::dom::create_element (" << endl @@ -341,14 +358,14 @@ namespace CXX << (ns ? strlit (ns) + L",\n" : L"") << "e));" << endl - << "s << *b;" + << "s << " << x << ";" << "}" << "else" << endl << "tsm.serialize (" << endl << strlit (e.name ()) << "," << endl << strlit (ns) << "," << endl << (e.global_p () ? "true" : "false") << ", " << - (e.qualified_p () ? "true" : "false") << ", e, *b);"; + (e.qualified_p () ? "true" : "false") << ", e, " << x << ");"; } else { @@ -363,30 +380,29 @@ namespace CXX { case st_other: { - os << "s << *b;"; + os << "s << " << x << ";"; break; } case st_double: { - os << "s << " << as_double_type << " (*b);"; + os << "s << " << as_double_type << " (" << x << ");"; break; } case st_decimal: { - os << "s << " << as_decimal_type << " (*b);"; + os << "s << " << as_decimal_type << " (" << x << ");"; break; } } } - - os << "}"; } else if (min (e) == 0) { // optional // - os << "if (i." << aname << " ())" - << "{"; + if (!ordered) + os << "if (i." << aname << " ())" + << "{"; if (poly) { @@ -436,8 +452,6 @@ namespace CXX } } } - - os << "}"; } else { @@ -465,8 +479,10 @@ namespace CXX } else { - os << "{" - << xerces_ns << "::DOMElement& s (" << endl + if (!ordered) + os << "{"; + + os << xerces_ns << "::DOMElement& s (" << endl << "::xsd::cxx::xml::dom::create_element (" << endl << strlit (e.name ()) << "," << endl << (ns ? strlit (ns) + L",\n" : L"") @@ -491,13 +507,26 @@ namespace CXX break; } } - - os << "}"; } } - if (poly) + if (ordered) + { + // See comment for bool text (false); below. + // + if (mixed_p (c) && c.context ().get ("ordered-start") != 1) + os << "text = true;"; + + os << "continue;" + << "}"; + } + else + { os << "}"; + + if (poly && (max (e) != 1 || min (e) == 0)) + os << "}"; // There is no extra block for poly one. + } } private: @@ -514,48 +543,77 @@ namespace CXX virtual void traverse (Type& a) { + SemanticGraph::Complex& c ( + dynamic_cast (a.scope ())); + + bool ordered (ordered_p (c)); + String const& aname (eaname (a)); os << "// " << ename (a) << endl << "//" << endl; + if (ordered) + os << "case " << scope << "::" << + a.context ().get ("ordered-id-name") << ":"; + if (max (a) != 1) { // sequence // - os << "for (" << scope << "::" << econst_iterator (a) << endl - << "b (i." << aname << " ().begin ()), " << - "n (i." << aname << " ().end ());" << endl - << "b != n; ++b)" - << "{" + if (!ordered) + os << "for (" << scope << "::" << econst_iterator (a) << endl + << "b (i." << aname << " ().begin ()), " << + "n (i." << aname << " ().end ());" << endl + << "b != n; ++b)"; + + os << "{" << "e.appendChild (" << endl << "e.getOwnerDocument ()->importNode (" << endl << "const_cast< " << xerces_ns << - "::DOMElement* > (&(*b)), true));" - << "}"; + "::DOMElement* > (&(" << + (ordered ? (L"i." + aname + L" ()[b->index]") : L"*b") << + ")), true));"; } else if (min (a) == 0) { // optional // - os << "if (i." << aname << " ())" - << "{" + if (!ordered) + os << "if (i." << aname << " ())"; + + os << "{" << "e.appendChild (" << endl << "e.getOwnerDocument ()->importNode (" << endl << "const_cast< " << xerces_ns << "::DOMElement* > (&(*i." << - aname << " ())), true));" - << "}"; + aname << " ())), true));"; } else { // one // + if (ordered) + os << "{"; + os << "e.appendChild (" << endl << "e.getOwnerDocument ()->importNode (" << endl << "const_cast< " << xerces_ns << "::DOMElement* > (&(i." << aname << " ())), true));" << endl; } + + if (ordered) + { + // See comment for bool text (false); below. + // + if (mixed_p (c) && c.context ().get ("ordered-start") != 1) + os << "text = true;"; + + os << "continue;"; + } + + if (ordered || max (a) != 1 || min (a) == 0) + os << "}"; } private: @@ -702,6 +760,8 @@ namespace CXX virtual void traverse (Type& c) { + SemanticGraph::Context& ctx (c.context ()); + String name (ename (c)); // If renamed name is empty then we do not need to generate @@ -740,22 +800,107 @@ namespace CXX } { + bool o (ordered_p (c)); + size_t start, count; + + if (o) + { + start = ctx.get ("ordered-start"); + count = ctx.get ("ordered-count"); + + if (start != count) + { + String const& ci (ctx.get ("order-const-iterator")); + String const& an (ctx.get ("order-aname")); + + // If we have mixed content and a base, then we have to + // skip the text content until we serialize one of "our" + // elements. + // + if (mixed_p (c) && start != 1) + os << "bool text (false);" + << endl; + + os << "for (" << name << "::" << ci << endl + << "b (i." << an << " ().begin ()), n (i." << an << + " ().end ());" << endl + << "b != n; ++b)" + << "{" + << "switch (b->id)" + << "{"; + } + } + Traversal::Names names; Any any (*this, name); Element element (*this, name); - Attribute attribute (*this, name); names >> element; - names >> attribute; if (options.generate_wildcard ()) names >> any; Complex::names (c, names); + + if (o) + { + if (start != count) + { + if (mixed_p (c)) + { + //@@ propagate mixed-ordered-id to derived + + os << "// text_content" << endl + << "//" << endl + << "case " << name << "::" << + ctx.get ("mixed-ordered-id-name") << ":" + << "{"; + + // See the comment above. + // + if (start != 1) + os << "if (text)" << endl; + + os << "e.appendChild (" << endl + << "e.getOwnerDocument ()->createTextNode (" << endl + << "::xsd::cxx::xml::string (" << endl + << "i." << ctx.get ("mixed-aname") << + " ()[b->index].c_str ()).c_str ()));"; + + // os << "e << i." << ctx.get ("mixed-aname") << + // " ()[b->index];"; + + os << "continue;" + << "}"; + } + + // Ignore content before our id range and stop serializing + // if we see anything past. This handles inheritance. + // + os << "default:" + << "{"; + + if (start != 1) + os << "if (b->id < " << start << "UL)" << endl + << "continue;"; + + os << "break;" // Stop (see break below). + << "}"; + + os << "}" // switch + << "break;" // Unknown element past our elements. + << "}"; // for + } + } } - os << "}"; + { + Attribute attribute (*this, name); + Traversal::Names names (attribute); + Complex::names (c, names); + } + os << "}"; bool simple (true); { diff --git a/xsd/cxx/tree/tree-header.cxx b/xsd/cxx/tree/tree-header.cxx index 83a2d1f..e6c2411 100644 --- a/xsd/cxx/tree/tree-header.cxx +++ b/xsd/cxx/tree/tree-header.cxx @@ -1076,7 +1076,7 @@ namespace CXX } bool def_attr (m.default_p () && - m.is_a ()); + m.is_a ()); if (max (m) != 1) { @@ -1784,6 +1784,9 @@ namespace CXX if (skip (m)) return; + SemanticGraph::Complex& c ( + dynamic_cast (m.scope ())); + String const& type (etype (m)); bool el (m.is_a ()); @@ -1826,7 +1829,7 @@ namespace CXX else { os << "// " << comment (m.name ()) << endl - << "// " << endl; + << "//" << endl; } // Typedefs. @@ -1872,7 +1875,7 @@ namespace CXX << " */" << endl; } - // IntelliSense does not not like aliases and fully-qualified + // IntelliSense does not like aliases and fully-qualified // names here. // if (!isense) @@ -1941,6 +1944,24 @@ namespace CXX os << " > " << etraits (m) << ";" << endl; + + // Element id. + // + if (el && ordered_p (c)) + { + if (doxygen) + os << "/**" << endl + << " * @brief Element id used for capturing content " << + "order." << endl + << " */" << endl; + + SemanticGraph::Context& ctx (m.context ()); + + os << "static const ::std::size_t " << + ctx.get ("ordered-id-name") << " = " << + ctx.get ("ordered-id") << "UL;" << endl; + } + member_function_.traverse (m); if (doxygen) @@ -1970,6 +1991,9 @@ namespace CXX virtual void traverse (SemanticGraph::Any& a) { + SemanticGraph::Complex& c ( + dynamic_cast (a.scope ())); + if (doxygen) { os << "/**" << endl @@ -1990,7 +2014,7 @@ namespace CXX else { os << "// " << ename (a) << endl - << "// " << endl; + << "//" << endl; } // Typedefs. @@ -2060,6 +2084,23 @@ namespace CXX os << endl; } + // Wildcard id. + // + if (ordered_p (c)) + { + if (doxygen) + os << "/**" << endl + << " * @brief Wildcard id used for capturing content " << + "order." << endl + << " */" << endl; + + SemanticGraph::Context& ctx (a.context ()); + + os << "static const ::std::size_t " << + ctx.get ("ordered-id-name") << " = " << + ctx.get ("ordered-id") << "UL;" << endl; + } + any_function_.traverse (a); if (doxygen) @@ -2095,7 +2136,7 @@ namespace CXX else { os << "// " << ename (a) << endl - << "// " << endl; + << "//" << endl; } if (doxygen) @@ -2288,6 +2329,8 @@ namespace CXX if (renamed_type (c, name) && !name) return; + SemanticGraph::Context& ctx (c.context ()); + bool has_members (has (c)); bool hae (has (c)); @@ -2295,6 +2338,9 @@ namespace CXX bool gen_wildcard (options.generate_wildcard ()); + bool mixed (mixed_p (c) && !ctx.count ("mixed-in-base")); + bool ordered (ordered_p (c) && !ctx.count ("order-in-base")); + bool simple (true); { IsSimpleType t (simple); @@ -2344,18 +2390,149 @@ namespace CXX // names (c, names_); - // dom_document accessors. + // Mixed content. // - if (edom_document_member_p (c)) + if (mixed) { + String const& type (ctx.get ("mixed-type")); + String const& cont (ctx.get ("mixed-container")); + String const& iter (ctx.get ("mixed-iterator")); + String const& citer (ctx.get ("mixed-const-iterator")); - if (!doxygen) - { - os << "// DOMDocument for wildcard content." << endl + if (doxygen) + os << "/**" << endl + << " * @name " << comment ("text_content") << endl + << " *" << endl + << " * @brief Accessor and modifier functions for text " << + "content." << endl + << " */" << endl + << "//@{" << endl; + else + os << "// text_content" << endl << "//" << endl; - } + + // Typedefs. + // + bool isense (options.generate_intellisense ()); + + if (doxygen) + os << endl + << "/**" << endl + << " * @brief Text content type." << endl + << " */" << endl; + + os << "typedef " << xs_string_type << " " << type << ";"; + + if (doxygen) + os << endl + << "/**" << endl + << " * @brief Text content sequence container type." << endl + << " */" << endl; + + os << "typedef ::xsd::cxx::tree::sequence< " << type << + " > " << cont << ";"; + + + if (doxygen) + os << endl + << "/**" << endl + << " * @brief Text content iterator type." << endl + << " */" << endl; + + // IntelliSense does not like aliases and fully-qualified + // names here. + // + if (!isense) + os << "typedef " << cont << "::iterator " << iter<< ";"; + else + os << "typedef ::xsd::cxx::tree::sequence< " << type << + " >::iterator " << iter << ";"; + + if (doxygen) + os << endl + << "/**" << endl + << " * @brief Text content constant iterator type." << endl + << " */" << endl; + + if (!isense) + os << "typedef " << cont << "::const_iterator " << citer<< ";"; + else + os << "typedef ::xsd::cxx::tree::sequence< " << type << + " >::const_iterator " << citer << ";"; + + os << endl; + + // Content id. + // + if (doxygen) + os << "/**" << endl + << " * @brief Text content id used for capturing content " << + "order." << endl + << " */" << endl; + + os << "static const ::std::size_t " << + ctx.get ("mixed-ordered-id-name") << " = " << + ctx.get ("mixed-ordered-id") << "UL;" << endl; + + // Accessors and modifiers. + // + String const& aname (ctx.get ("mixed-aname")); + String const& mname (ctx.get ("mixed-mname")); + + if (doxygen) + os << "/**" << endl + << " * @brief Return a read-only (constant) reference " << + "to the text" << endl + << " * content sequence." << endl + << " *" << endl + << " * @return A constant reference to the sequence " << + "container." << endl + << " */" << endl; + + os << "const " << cont << "&" << endl + << aname << " () const;" + << endl; + + if (doxygen) + os << "/**" << endl + << " * @brief Return a read-write reference to the " << + "text content" << endl + << " * sequence." << endl + << " *" << endl + << " * @return A reference to the sequence container." << endl + << " */" << endl; + + os << cont << "&" << endl + << aname << " ();" + << endl; + + if (doxygen) + os << "/**" << endl + << " * @brief Copy elements from a given sequence." << endl + << " *" << endl + << " * @param s A sequence to copy entries from." << endl + << " *" << endl + << " * For each element in @a s this function " << + "add it to the sequence." << endl + << " * Note that this operation " << + "completely changes the sequence and" << endl + << " * all old elements will be lost." << endl + << " */" << endl; + + os << "void" << endl + << mname << " (const " << cont << "& s);" + << endl; if (doxygen) + os << "//@}" << endl + << endl; + } + + // dom_document accessors. + // + if (edom_document_member_p (c)) + { + if (doxygen) { os << "/**" << endl << " * @brief Return a read-only (constant) reference " << @@ -2369,6 +2546,9 @@ namespace CXX << " * the raw XML content corresponding to wildcards." << endl << " */" << endl; } + else + os << "// DOMDocument for wildcard content." << endl + << "//" << endl; os << "const " << xerces_ns << "::DOMDocument&" << endl << edom_document (c) << " () const;" @@ -2393,6 +2573,137 @@ namespace CXX << endl; } + // Order container. + // + if (ordered) + { + String const& type (ctx.get ("order-type")); + String const& cont (ctx.get ("order-container")); + String const& iter (ctx.get ("order-iterator")); + String const& citer (ctx.get ("order-const-iterator")); + + String const ct (options.order_container_specified () + ? options.order_container () + : "::std::vector"); + + if (doxygen) + os << "/**" << endl + << " * @name " << comment ("content_order") << endl + << " *" << endl + << " * @brief Accessor and modifier functions for content " << + "order." << endl + << " */" << endl + << "//@{" << endl; + else + os << "// content_order" << endl + << "//" << endl; + + // Typedefs. + // + bool isense (options.generate_intellisense ()); + + if (doxygen) + os << endl + << "/**" << endl + << " * @brief Content order entry type." << endl + << " */" << endl; + + os << "typedef " << ns_name (xs_ns ()) << "::" << + xs_ns ().context ().get ("content-order") << " " << + type << ";"; + + if (doxygen) + os << endl + << "/**" << endl + << " * @brief Content order sequence container type." << endl + << " */" << endl; + + os << "typedef " << ct << "< " << type << " > " << cont << ";"; + + + if (doxygen) + os << endl + << "/**" << endl + << " * @brief Content order iterator type." << endl + << " */" << endl; + + // IntelliSense does not like aliases and fully-qualified + // names here. + // + if (!isense) + os << "typedef " << cont << "::iterator " << iter<< ";"; + else + os << "typedef " << ct << "< " << type << " >::iterator " << + iter << ";"; + + if (doxygen) + os << endl + << "/**" << endl + << " * @brief Content order constant iterator type." << endl + << " */" << endl; + + if (!isense) + os << "typedef " << cont << "::const_iterator " << citer<< ";"; + else + os << "typedef " << ct << "< " << type << + " >::const_iterator " << citer << ";"; + + os << endl; + + // Accessors and modifiers. + // + String const& aname (ctx.get ("order-aname")); + String const& mname (ctx.get ("order-mname")); + + if (doxygen) + os << "/**" << endl + << " * @brief Return a read-only (constant) reference " << + "to the content" << endl + << " * order sequence." << endl + << " *" << endl + << " * @return A constant reference to the sequence " << + "container." << endl + << " */" << endl; + + os << "const " << cont << "&" << endl + << aname << " () const;" + << endl; + + if (doxygen) + os << "/**" << endl + << " * @brief Return a read-write reference to the " << + "content order" << endl + << " * sequence." << endl + << " *" << endl + << " * @return A reference to the sequence container." << endl + << " */" << endl; + + os << cont << "&" << endl + << aname << " ();" + << endl; + + if (doxygen) + os << "/**" << endl + << " * @brief Copy elements from a given sequence." << endl + << " *" << endl + << " * @param s A sequence to copy entries from." << endl + << " *" << endl + << " * For each element in @a s this function " << + "add it to the sequence." << endl + << " * Note that this operation " << + "completely changes the sequence and" << endl + << " * all old elements will be lost." << endl + << " */" << endl; + + os << "void" << endl + << mname << " (const " << cont << "& s);" + << endl; + + if (doxygen) + os << "//@}" << endl + << endl; + } + if (doxygen) { os << "/**" << endl @@ -3042,7 +3353,7 @@ namespace CXX // Data members and implementation functions. // - if (has_members || hae || (haa && gen_wildcard)) + if (has_members || hae || (haa && gen_wildcard) || ordered || mixed) { os << "// Implementation." << endl << "//" << endl; @@ -3052,9 +3363,10 @@ namespace CXX << "//@cond" << endl << endl; - if (!options.suppress_parsing ()) + if (!options.suppress_parsing () && + (has_members || hae || (haa && gen_wildcard) || mixed)) { - // parse (xercesc::DOMElement) + // parse () // os << "protected:" << endl << "void" << endl @@ -3082,7 +3394,7 @@ namespace CXX } } - // + // DOM document. // if (edom_document_member_p (c)) { @@ -3091,6 +3403,24 @@ namespace CXX << endl; } + // Mixed text content. + // + if (mixed) + { + os << ctx.get ("mixed-container") << " " << + ctx.get ("mixed-member") << ";" + << endl; + } + + // Order container. + // + if (ordered) + { + os << ctx.get ("order-container") << " " << + ctx.get ("order-member") << ";" + << endl; + } + // // names (c, names_data_); @@ -3811,6 +4141,15 @@ namespace CXX if (ctx.std >= cxx_version::cxx11) ctx.os << "#include // std::move" << endl; + if (!ctx.options.ordered_type ().empty () || + ctx.options.ordered_type_all ()) + { + ctx.os << "#include // std::size_t" << endl; + + if (!ctx.options.order_container_specified ()) + ctx.os << "#include " << endl; + } + ctx.os << endl; if (ctx.char_type == L"char" && ctx.char_encoding != L"custom") diff --git a/xsd/cxx/tree/tree-inline.cxx b/xsd/cxx/tree/tree-inline.cxx index 1c01e5a..e6ed756 100644 --- a/xsd/cxx/tree/tree-inline.cxx +++ b/xsd/cxx/tree/tree-inline.cxx @@ -891,6 +891,41 @@ namespace CXX Complex::names (c, names); + // Mixed text content. + // + if (mixed_p (c) && !c.context ().count ("mixed-in-base")) + { + SemanticGraph::Context& ctx (c.context ()); + + String const& cont (ctx.get ("mixed-container")); + String const& memb (ctx.get ("mixed-member")); + + String const& aname (ctx.get ("mixed-aname")); + String const& mname (ctx.get ("mixed-mname")); + + os << inl + << "const " << name << "::" << cont << "& " << + name << "::" << endl + << aname << " () const" + << "{" + << "return this->" << memb << ";" + << "}"; + + os << inl + << name << "::" << cont << "& " << name << "::" << endl + << aname << " ()" + << "{" + << "return this->" << memb << ";" + << "}"; + + os << inl + << "void " << name << "::" << endl + << mname << " (const " << cont << "& s)" + << "{" + << "this->" << memb << " = s;" + << "}"; + } + // dom_document accessors. // if (edom_document_member_p (c)) @@ -900,14 +935,49 @@ namespace CXX name << "::" << endl << edom_document (c) << " () const" << "{" - << "return *" << edom_document_member (c) << ";" + << "return *this->" << edom_document_member (c) << ";" << "}"; os << inl << xerces_ns << "::DOMDocument& " << name << "::" << endl << edom_document (c) << " ()" << "{" - << "return *" << edom_document_member (c) << ";" + << "return *this->" << edom_document_member (c) << ";" + << "}"; + } + + // Order container. + // + if (ordered_p (c) && !c.context ().count ("order-in-base")) + { + SemanticGraph::Context& ctx (c.context ()); + + String const& cont (ctx.get ("order-container")); + String const& memb (ctx.get ("order-member")); + + String const& aname (ctx.get ("order-aname")); + String const& mname (ctx.get ("order-mname")); + + os << inl + << "const " << name << "::" << cont << "& " << + name << "::" << endl + << aname << " () const" + << "{" + << "return this->" << memb << ";" + << "}"; + + os << inl + << name << "::" << cont << "& " << name << "::" << endl + << aname << " ()" + << "{" + << "return this->" << memb << ";" + << "}"; + + os << inl + << "void " << name << "::" << endl + << mname << " (const " << cont << "& s)" + << "{" + << "this->" << memb << " = s;" << "}"; } diff --git a/xsd/cxx/tree/tree-source.cxx b/xsd/cxx/tree/tree-source.cxx index 036f7ae..110cb25 100644 --- a/xsd/cxx/tree/tree-source.cxx +++ b/xsd/cxx/tree/tree-source.cxx @@ -693,6 +693,9 @@ namespace CXX if (skip (e)) return; + SemanticGraph::Complex& c ( + dynamic_cast (e.scope ())); + String const& member (emember (e)); String tr (etraits (e)); // traits type name @@ -849,6 +852,27 @@ namespace CXX } } + // Capture order. + // + if (ordered_p (c)) + { + SemanticGraph::Context& ctx (c.context ()); + + String const& t (ctx.get ("order-type")); + String const& m (ctx.get ("order-member")); + + os << "this->" << m << ".push_back (" << endl + << t << " (" << + e.context ().get ("ordered-id-name"); + + // sequence + // + if (max (e) != 1) + os << ", " << "this->" << member << ".size () - 1"; + + os << "));"; + } + os << "continue;"; // End of check block. @@ -920,10 +944,11 @@ namespace CXX { String const& member (emember (a)); + SemanticGraph::Complex& c ( + dynamic_cast (a.scope ())); + String const& ns (a.definition_namespace ().name ()); - String const& dom_doc ( - edom_document ( - dynamic_cast (a.scope ()))); + String const& dom_doc (edom_document (c)); os << "// " << ename (a) << endl << "//" << endl @@ -1004,7 +1029,7 @@ namespace CXX { // sequence // - os << "this->" << member << " .push_back (r);"; + os << "this->" << member << ".push_back (r);"; } else if (min (a) == 0) { @@ -1019,6 +1044,31 @@ namespace CXX os << "this->" << member << ".set (r);"; } + // Capture order. + // + if (ordered_p (c)) + { + SemanticGraph::Context& ctx (c.context ()); + + String const& t (ctx.get ("order-type")); + String const& m (ctx.get ("order-member")); + + os << "this->" << m << ".push_back (" << endl + << t << " (" << + a.context ().get ("ordered-id-name") << ", "; + + if (max (a) != 1) + // sequence + // + os << "this->" << member << ".size () - 1"; + else + // optional & one + // + os << "0"; + + os << "));"; + } + os << "continue;"; // End of check block. @@ -2042,6 +2092,9 @@ namespace CXX virtual void traverse (SemanticGraph::Complex& c) { + if (mixed_p (c) && !c.context ().count ("mixed-in-base")) + has_el_ = true; + names (c); if (!(has_el_ && has_at_)) @@ -2232,6 +2285,11 @@ namespace CXX if (renamed_type (c, name) && !name) return; + SemanticGraph::Context& ctx (c.context ()); + + bool mixed (mixed_p (c) && !ctx.count ("mixed-in-base")); + bool ordered (ordered_p (c) && !ctx.count ("order-in-base")); + bool string_based (false); { IsStringBasedType t (string_based); @@ -2348,6 +2406,12 @@ namespace CXX " > ())"; } + if (mixed) + { + os << "," << endl + << " " << ctx.get ("mixed-member") << " (this)"; + } + names (c, default_ctor_init_names_); os << "{"; @@ -2403,6 +2467,12 @@ namespace CXX " > ())"; } + if (mixed) + { + os << "," << endl + << " " << ctx.get ("mixed-member") << " (this)"; + } + names (c, ctor_names_); os << "{"; @@ -2437,6 +2507,12 @@ namespace CXX " > ())"; } + if (mixed) + { + os << "," << endl + << " " << ctx.get ("mixed-member") << " (this)"; + } + { CtorMember t (*this, at); Traversal::Names n (t); @@ -2481,6 +2557,12 @@ namespace CXX " > ())"; } + if (mixed) + { + os << "," << endl + << " " << ctx.get ("mixed-member") << " (this)"; + } + { CtorMember t (*this, at); Traversal::Names n (t); @@ -2529,6 +2611,12 @@ namespace CXX " > ())"; } + if (mixed) + { + os << "," << endl + << " " << ctx.get ("mixed-member") << " (this)"; + } + names (c, ctor_names_); os << "{"; @@ -2568,6 +2656,12 @@ namespace CXX " > ())"; } + if (mixed) + { + os << "," << endl + << " " << ctx.get ("mixed-member") << " (this)"; + } + { CtorMember t (*this, at); Traversal::Names n (t); @@ -2616,6 +2710,12 @@ namespace CXX " > ())"; } + if (mixed) + { + os << "," << endl + << " " << ctx.get ("mixed-member") << " (this)"; + } + { CtorMember t (*this, at); Traversal::Names n (t); @@ -2673,6 +2773,8 @@ namespace CXX " > ())"; } + // Cannot be mixed. + names (c, ctor_names_); os << "{"; @@ -2714,6 +2816,8 @@ namespace CXX " > ())"; } + // Cannot be mixed. + names (c, ctor_names_); os << "{"; @@ -2752,6 +2856,8 @@ namespace CXX " > ())"; } + // Cannot be mixed. + names (c, ctor_names_); os << "{"; @@ -2795,6 +2901,12 @@ namespace CXX " > ())"; } + if (mixed) + { + os << "," << endl + << " " << ctx.get ("mixed-member") << " (this)"; + } + names (c, ctor_names_); os << "{"; @@ -2839,6 +2951,12 @@ namespace CXX " > ())"; } + if (mixed) + { + os << "," << endl + << " " << ctx.get ("mixed-member") << " (this)"; + } + { CtorMember t (*this, at); Traversal::Names n (t); @@ -2892,6 +3010,12 @@ namespace CXX " > ())"; } + if (mixed) + { + os << "," << endl + << " " << ctx.get ("mixed-member") << " (this)"; + } + { CtorMember t (*this, at); Traversal::Names n (t); @@ -2926,6 +3050,20 @@ namespace CXX " > ())"; } + if (mixed) + { + String const& m (ctx.get ("mixed-member")); + os << "," << endl + << " " << m << " (x." << m << ", f, this)"; + } + + if (ordered) + { + String const& m (ctx.get ("order-member")); + os << "," << endl + << " " << m << " (x." << m << ")"; + } + { CopyAny copy_any (*this, "x"); CopyMember copy_member (*this, "x"); @@ -2964,7 +3102,7 @@ namespace CXX << container << "* c)" << endl << ": " << base << " (e, f"; - if (he || ha || hae || (haa && gen_wildcard)) + if (he || ha || hae || (haa && gen_wildcard) || mixed) os << " | " << flags_type << "::base"; os << ", c)"; @@ -2977,6 +3115,12 @@ namespace CXX " > ())"; } + if (mixed) + { + os << "," << endl + << " " << ctx.get ("mixed-member") << " (this)"; + } + names (c, element_ctor_names_); os << "{"; @@ -2988,9 +3132,9 @@ namespace CXX bool base_has_el (false), base_has_at (false); // We are only interested in this information if we are - // generating out own parse(). + // generating our own parse(). // - if (he || ha || hae || (haa && gen_wildcard)) + if (he || ha || hae || (haa && gen_wildcard) || mixed) { if (c.inherits_p ()) { @@ -2999,13 +3143,14 @@ namespace CXX } } - //@@ throw if p is no exhausted at the end. + //@@ Throw if p is not exhausted at the end. // - if (he || ha || hae || (haa && gen_wildcard)) + if (he || ha || hae || (haa && gen_wildcard) || mixed) os << "if ((f & " << flags_type << "::base) == 0)" << "{" << parser_type << " p (e, " << - (he || hae || base_has_el ? "true, " : "false, ") << + (he || hae || base_has_el || mixed_p (c) ? "true, " : "false, ") << + (mixed_p (c) ? "true, " : "false, ") << (ha || (haa && gen_wildcard) || base_has_at ? "true" : "false") << ");" << "this->" << unclash (name, "parse") << " (p, f);" @@ -3051,7 +3196,7 @@ namespace CXX os << "}"; } - if (he || ha || hae || (haa && gen_wildcard)) + if (he || ha || hae || (haa && gen_wildcard) || mixed) { os << "void " << name << "::" << endl << unclash (name, "parse") << " (" << @@ -3066,17 +3211,45 @@ namespace CXX os << "this->" << base << "::parse (p, f);" << endl; - if (he || hae) + if (he || hae || mixed_p (c)) { - os << "for (; p.more_elements (); p.next_element ())" - << "{" - << "const " << xerces_ns << "::DOMElement& i (" << - "p.cur_element ());" - << "const " << qname_type << " n (" << endl - << "::xsd::cxx::xml::dom::name< " << char_type << " > (i));" - << endl; + bool m (mixed_p (c)); + + os << "for (; p.more_content (); p.next_content (" << + (m ? "true" : "false") << "))" + << "{"; - names (c, names_element_); + if (m) + { + String const& ma (ctx.get ("mixed-aname")); + String const& mi (ctx.get ("mixed-ordered-id-name")); + String const& oa (ctx.get ("order-aname")); + String const& ot (ctx.get ("order-type")); + + os << "if (p.cur_is_text ())" + << "{" + << "const " << xerces_ns << "::DOMText& t (" << + "p.cur_text ());" + << "this->" << ma << " ().push_back (" << endl + << "::xsd::cxx::xml::transcode<" << char_type << "> (" << + "t.getData (), t.getLength ()));" + << "this->" << oa << " ().push_back (" << endl + << ot << " (" << mi << "," << endl + << "this->" << ma << " ().size () - 1));" + << "continue;" + << "}"; + } + + if (he || hae) + { + os << "const " << xerces_ns << "::DOMElement& i (" << + "p.cur_element ());" + << "const " << qname_type << " n (" << endl + << "::xsd::cxx::xml::dom::name< " << char_type << " > (i));" + << endl; + + names (c, names_element_); + } os << "break;" << "}"; @@ -3152,7 +3325,23 @@ namespace CXX // Note that here we don't assign the DOMDocument that is // used to hold wildcard fragments. Each document has its // own copy. + + // Mixed text content. // + if (mixed) + { + String const& m (ctx.get ("mixed-member")); + os << "this->" << m << " = x." << m << ";"; + } + + // Order container. + // + if (ordered) + { + String const& m (ctx.get ("order-member")); + os << "this->" << m << " = x." << m << ";"; + } + names (c, assign_names_); os << "}" @@ -3203,7 +3392,7 @@ namespace CXX // Comparison operators. // if (options.generate_comparison () && - (he || ha || !c.inherits_p () || + (he || ha || mixed || ordered || !c.inherits_p () || ((hae || haa) && gen_wildcard))) { bool base_comp (false); @@ -3214,8 +3403,8 @@ namespace CXX test.dispatch (c.inherits ().base ()); } - bool has_body (he || ha || base_comp || - ((hae || haa) && gen_wildcard)); + bool has_body (he || ha || ordered || mixed || base_comp || + ((hae || haa) && gen_wildcard)); os << "bool" << endl << "operator== (const " << name << "&" << @@ -3233,6 +3422,22 @@ namespace CXX Complex::names (c, comparison_names_); } + if (mixed) + { + String const& an (ctx.get ("mixed-aname")); + os << "if (!(x." << an << " () == y." << an << " ()))" << endl + << "return false;" + << endl; + } + + if (ordered) + { + String const& an (ctx.get ("order-aname")); + os << "if (!(x." << an << " () == y." << an << " ()))" << endl + << "return false;" + << endl; + } + os << "return true;" << "}"; diff --git a/xsd/makefile b/xsd/makefile index 06ea02c..d57022d 100644 --- a/xsd/makefile +++ b/xsd/makefile @@ -31,6 +31,7 @@ cxx_tun += cxx/tree/elements.cxx \ cxx/tree/validator.cxx \ cxx/tree/counter.cxx \ cxx/tree/name-processor.cxx \ + cxx/tree/order-processor.cxx \ cxx/tree/polymorphism-processor.cxx \ cxx/tree/default-value.cxx \ cxx/tree/generator.cxx \ @@ -114,6 +115,7 @@ endif $(gen): cli := $(cli) $(gen): cli_options += \ -I $(src_base) \ +--generate-specifier \ --ostream-type ::std::wostream \ --exclude-base \ --suppress-undocumented \ diff --git a/xsd/options-parser.hxx b/xsd/options-parser.hxx index 10456e1..0f370cd 100644 --- a/xsd/options-parser.hxx +++ b/xsd/options-parser.hxx @@ -14,8 +14,9 @@ namespace cli struct parser { static void - parse (NarrowString& x, scanner& s) + parse (NarrowString& x, bool& xs, scanner& s) { + xs = true; const char* o (s.next ()); if (s.more ()) -- cgit v1.1