From e4c22d3686da0e973e21eae0561c1169c0eeff36 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 26 Mar 2009 17:09:53 +0200 Subject: Implement support for XML Schema polymorphism in C++/Hybrid examples/cxx/hybrid/polyroot/ examples/cxx/hybrid/polymorphism/: new examples tests/cxx/hybrid/polymorphism/: new tests --- documentation/cxx/hybrid/guide/index.xhtml | 397 ++++++++++++++++++++++++- documentation/cxx/parser/guide/index.xhtml | 4 +- documentation/cxx/serializer/guide/index.xhtml | 2 +- documentation/xsde.1 | 37 +++ documentation/xsde.xhtml | 34 ++- 5 files changed, 468 insertions(+), 6 deletions(-) (limited to 'documentation') diff --git a/documentation/cxx/hybrid/guide/index.xhtml b/documentation/cxx/hybrid/guide/index.xhtml index 62603dc..5871593 100644 --- a/documentation/cxx/hybrid/guide/index.xhtml +++ b/documentation/cxx/hybrid/guide/index.xhtml @@ -256,6 +256,7 @@ 3.4XML Schema Validation 3.564-bit Integer Type 3.6Parser and Serializer Reuse + 3.7Support for Polymorphism @@ -271,6 +272,7 @@ 4.6Modifying the Object Model 4.7Creating the Object Model from Scratch 4.8Customizing the Object Model + 4.9Polymorphic Object Models @@ -560,7 +562,7 @@ <greeting>Hello</greeting> <name>sun</name> - <name>earth</name> + <name>moon</name> <name>world</name> </hello> @@ -910,7 +912,7 @@ main (int argc, char* argv[]) <hello> <greeting>Hi</greeting> <name>sun</name> - <name>earth</name> + <name>moon</name> <name>world</name> <name>mars</name> </hello> @@ -1410,6 +1412,154 @@ namespace xml_schema "Serializer Reuse" in the Embedded C++/Serializer Mapping Getting Started Guide for details.

+

3.7 Support for Polymorphism

+ +

By default the XSD/e compiler generates non-polymorphic code. If your + vocabulary uses XML Schema polymorphism in the form of xsi:type + and/or substitution groups, then you will need to configure the XSD/e + runtime with support for polymorphism, compile your schemas with the + --generate-polymorphic option to produce polymorphism-aware + code, as well as pass true as the last argument to the + xml_schema::document_pimpl and + xml_schema::document_simpl constructors (see + Chapter 6, "Parsing and Serialization" for details). + If some of your schemas do not require support for polymorphism then + you can compile them with the --runtime-polymorphic option + and still use the XSD/e runtime configured with polymorphism support. +

+ +

The XSD/e compiler can often automatically determine which types are + polymorphic based on the substitution group declarations. However, + if your XML vocabulary is not using substitution groups or if + substitution groups are defined in a separate schema, then you will + need to use the --polymorphic-type option to specify + which types are polymorphic. When using this option you only need + to specify the root of a polymorphic type hierarchy and the XSD/e + compiler will assume that all the derived types are also polymorphic. + Also note that you need to specify this option when compiling every + schema file that references the polymorphic type. Consider the following + two schemas as an example:

+ +
+<!-- base.xsd -->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+  <xs:complexType name="base">
+    <xs:sequence>
+      <xs:element name="b" type="xs:int"/>
+    </xs:sequence>
+  </xs:complexType>
+
+  <!-- substitution group root -->
+  <xs:element name="base" type="base"/>
+
+</xs:schema>
+  
+ +
+<!-- derived.xsd -->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+  <include schemaLocation="base.xsd"/>
+
+  <xs:complexType name="derived">
+    <xs:complexContent>
+      <xs:extension base="base">
+        <xs:sequence>
+          <xs:element name="d" type="xs:string"/>
+        </xs:sequence>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+
+  <xs:element name="derived" type="derived" substitutionGroup="base"/>
+
+</xs:schema>
+  
+ +

In this example we need to specify "--polymorphic-type base" + when compiling both schemas because the substitution group is declared + in a schema other than the one defining type base.

+ +

Another issue that may arise when compiling polymorphic schemas is + the situation where the XSD/e compiler is unaware of all the + derivations of a polymorphic type while generating parser and + serializer aggregates. As a result, the generated code may not + be able to parse and serialize these "invisible" to the compiler + types. The following example will help illustrate this case. + Consider a modified version of base.xsd from the + above example:

+ +
+<!-- base.xsd -->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+  <xs:complexType name="base">
+    <xs:sequence>
+      <xs:element name="b" type="xs:int"/>
+    </xs:sequence>
+  </xs:complexType>
+
+  <!-- substitution group root -->
+  <xs:element name="base" type="base"/>
+
+  <xs:complexType name="root">
+    <xs:sequence>
+      <xs:element ref="base" maxOccurs="unbounded"/>
+    </xs:sequence>
+  </xs:complexType>
+
+  <!-- document root -->
+  <xs:element name="root" type="root"/>
+
+</xs:schema>
+  
+ +

Suppose we compile this schema as follows:

+ +
+$ xsde cxx-hybrid --generate-parser --generate-serializer \
+--generate-polymorphic --polymorphic-type base \
+--generate-aggregate --root-element root base.xsd
+  
+ +

The resulting parser and serializer aggregates for the root + element will not include the parser and serializer for the + derived type that can be used instead of the + base type. This is because the XSD/e compiler + has no knowledge of the derived's existence when + compiling base.xsd.

+ +

There are two ways to overcome this problem. The easier but + potentially slower approach is to compile all your schemas + at once, for example:

+ +
+$ xsde cxx-hybrid --generate-parser --generate-serializer \
+--generate-polymorphic --polymorphic-type base \
+--generate-aggregate --root-element root base.xsd derived.xsd
+  
+ +

This will make sure the XSD/e compiler "sees" all the derivations + of the polymorphic types. The other approach allows + you to explicitly specify, with the --polymorphic-schema + option, additional schemas that may contain derivations of the + polymorphic types. Using this approach we would compile + base.xsd and derived.xsd like this:

+ +
+$ xsde cxx-hybrid --generate-parser --generate-serializer \
+--generate-polymorphic --polymorphic-type base \
+--generate-aggregate --root-element root \
+--polymorphic-schema derived.xsd base.xsd
+
+$ xsde cxx-hybrid --generate-parser --generate-serializer \
+--generate-polymorphic --polymorphic-type base derived.xsd
+  
+ +

For information on how to use object models with polymorphic types, + refer to Section 4.9, "Polymorphic Object Models".

+ @@ -1672,6 +1822,9 @@ private:
  • it is recursive (that is, one of its elements contains a reference, directly or indirectly, to the type itself)
  • + +
  • it is polymorphic (see Section 4.9, "Polymorphic + Object Models" for details)
  • The following build-in XML Schema types are variable-length: @@ -3536,6 +3689,246 @@ for (people::person_iterator i = ps.begin (); i != ps.end (); ++i) +

    4.9 Polymorphic Object Models

    + +

    When generating polymorphism-aware code (see Section + 3.7, "Support for Polymorphism"), some objects + in the resulting object model will be polymorphic. By polymorphic + we mean that the object's (static) type as specified in the + object model's interface may differ from the object's actual + (dynamic) type. Because of this, it may be necessary to discover + the object's actual type at runtime and cast it to this type to + gain access to the object's extended interface. Consider the + following schema as an example:

    + +
    +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    +
    +  <xs:complexType name="person">
    +    <xs:sequence>
    +      <xs:element name="name" type="xs:string"/>
    +    </xs:sequence>
    +  </xs:complexType>
    +
    +  <!-- substitution group root -->
    +  <xs:element name="person" type="person"/>
    +
    +  <xs:complexType name="superman">
    +    <xs:complexContent>
    +      <xs:extension base="person">
    +        <xs:attribute name="can-fly" type="xs:boolean"/>
    +      </xs:extension>
    +    </xs:complexContent>
    +  </xs:complexType>
    +
    +  <xs:element name="superman"
    +              type="superman"
    +              substitutionGroup="person"/>
    +
    +  <xs:complexType name="batman">
    +    <xs:complexContent>
    +      <xs:extension base="superman">
    +        <xs:attribute name="wing-span" type="xs:unsignedInt"/>
    +      </xs:extension>
    +    </xs:complexContent>
    +  </xs:complexType>
    +
    +  <xs:element name="batman"
    +              type="batman"
    +              substitutionGroup="superman"/>
    +
    +  <xs:complexType name="supermen">
    +    <xs:sequence>
    +      <xs:element ref="person" maxOccurs="unbounded"/>
    +    </xs:sequence>
    +  </xs:complexType>
    +
    +  <xs:element name="supermen" type="supermen"/>
    +
    +</xs:schema>
    +  
    + +

    Conforming XML documents can use the superman + and batman types in place of the person + type either by specifying the type with the xsi:type + attributes or by using the elements from the substitution + group, for instance:

    + + +
    +<supermen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    +
    +  <person>
    +    <name>John Doe</name>
    +  </person>
    +
    +  <superman can-fly="false">
    +    <name>James "007" Bond</name>
    +  </superman>
    +
    +  <superman can-fly="true" wing-span="10" xsi:type="batman">
    +    <name>Bruce Wayne</name>
    +  </superman>
    +
    +</supermen>
    +  
    + +

    When compiling the schema above with the + --generate-polymorphic option, the XSD/e compiler + automatically detects that the type hierarchy starting with + the person type is polymorphic. A polymorphic + type is always variable-length which means objects of polymorphic + types are allocated dynamically and are stored and passed around + as pointers or references. A polymorphic type also defines a + virtual destructor which allows you to delete an instance of a + polymorphic type via a pointer to its base. The following code + fragment shows how we can parse, access, modify, and serialize + the above XML document:

    + +
    +// Parse.
    +//
    +supermen_paggr supermen_p;
    +
    +// The last argument to the document's constructor indicates that we
    +// are parsing polymorphic XML documents.
    +//
    +xml_schema::document_pimpl doc_p (
    +  supermen_p.root_parser (),
    +  supermen_p.root_name (),
    +  true);
    +
    +supermen_p.pre ();
    +doc_p.parse ("supermen.xml");
    +auto_ptr<supermen> sm (supermen_p.post ());
    +
    +// Print what we've got.
    +//
    +for (supermen::person_iterator i = sm->person ().begin ();
    +     i != sm->person ().end ();
    +     ++i)
    +{
    +  person& p = *i;
    +
    +  if (batman* b = dynamic_cast<batman*> (&p))
    +  {
    +    cerr << b->name () << ", batman, wing span " <<
    +      b->wing_span () << endl;
    +  }
    +  else if (superman* s = dynamic_cast<superman*> (&p))
    +  {
    +    cerr << s->name () << ", ";
    +
    +    if (s->can_fly ())
    +      cerr << "flying ";
    +
    +    cerr << "superman" << endl;
    +  }
    +  else
    +  {
    +    cerr << p.name () << ", ordinary person" << endl;
    +  }
    +}
    +
    +// Add another superman entry.
    +//
    +auto_ptr<superman> s (new superman);
    +s->name ("Clark Kent");
    +s->can_fly (true);
    +sm->person ().push_back (s.release ());
    +
    +// Serialize.
    +//
    +supermen_saggr supermen_s;
    +
    +// The last argument to the document's constructor indicates that we
    +// are serializing polymorphic XML documents.
    +//
    +xml_schema::document_simpl doc_s (
    +  supermen_s.root_serializer (),
    +  supermen_s.root_name (),
    +  true);
    +
    +doc_s.add_no_namespace_schema ("supermen.xsd");
    +
    +supermen_s.pre (*sm);
    +doc_s.serialize (cout);
    +supermen_s.post ();
    +  
    + +

    In the example above we used the standard C++ RTTI mechanism + to detect the object's actual (dynamic) type. If RTTI is not + available on your platform, then you can request the generation + of custom runtime type information for polymorphic types + with the --generate-typeinfo XSD/e compiler + option. When this option is specified, each polymorphic + type provides the following two public functions:

    + +
    +virtual const std::string&
    +_dynamic_type () const;
    +
    +static const std::string&
    +_static_type ();
    +  
    + +

    Or, if STL is disabled (Section 3.1, "Standard Template + Library"), the following two functions:

    + +
    +virtual const char*
    +_dynamic_type () const;
    +
    +static const char*
    +_static_type ();
    +  
    + +

    The _dynamic_type() function returns the object's + dynamic type id. The _static_type() function + returns the type's static id that can be compared to the + dynamic id. The following code fragment shows how + we can change the previous example to use custom type information + instead of C++ RTTI:

    + +
    +for (supermen::person_iterator i = sm->person ().begin ();
    +     i != sm->person ().end ();
    +     ++i)
    +{
    +  person& p = *i;
    +  const string& dt = p._dynamic_type ();
    +
    +  if (dt == batman::_static_type ())
    +  {
    +    batman& b = static_cast<batman&> (p)
    +    cerr << b.name () << ", batman, wing span " <<
    +      b.wing_span () << endl;
    +  }
    +  else if (dt == superman::_static_type ())
    +  {
    +    superman& s = static_cast<superman&> (p)
    +    cerr << s.name () << ", ";
    +
    +    if (s.can_fly ())
    +      cerr << "flying ";
    +
    +    cerr << "superman" << endl;
    +  }
    +  else
    +  {
    +    cerr << p.name () << ", ordinary person" << endl;
    +  }
    +}
    +  
    + +

    Most of the code presented in this section is taken from the + polymorphism example which can be found in the + examples/cxx/hybrid/ directory of the XSD/e distribution. + Handling of xsi:type and substitution groups when used + on root elements requires a number of special actions as shown in + the polyroot example.

    + + diff --git a/documentation/cxx/parser/guide/index.xhtml b/documentation/cxx/parser/guide/index.xhtml index 85b5092..ac69845 100644 --- a/documentation/cxx/parser/guide/index.xhtml +++ b/documentation/cxx/parser/guide/index.xhtml @@ -522,7 +522,7 @@ <greeting>Hello</greeting> <name>sun</name> - <name>earth</name> + <name>moon</name> <name>world</name> </hello> @@ -2322,7 +2322,7 @@ protected: runtime with support for polymorphism, compile your schemas with the --generate-polymorphic option to produce polymorphism-aware code, as well as pass true as the last argument to the - xml_schema::document's constructors. If some of your + xml_schema::document_pimpl's constructors. If some of your schemas do not require support for polymorphism then you can compile them with the --runtime-polymorphic option and still use the XSD/e runtime configured with polymorphism support. diff --git a/documentation/cxx/serializer/guide/index.xhtml b/documentation/cxx/serializer/guide/index.xhtml index 4deacf0..bd681cc 100644 --- a/documentation/cxx/serializer/guide/index.xhtml +++ b/documentation/cxx/serializer/guide/index.xhtml @@ -529,7 +529,7 @@ <greeting>Hello</greeting> <name>sun</name> - <name>earth</name> + <name>moon</name> <name>world</name> </hello> diff --git a/documentation/xsde.1 b/documentation/xsde.1 index f9fe998..62cc55d 100644 --- a/documentation/xsde.1 +++ b/documentation/xsde.1 @@ -1005,6 +1005,43 @@ Suppress the generation of parser and serializer reset code. Reset support allows you to reuse parsers and serializers after an error. +.IP "\fB\--generate-polymorphic\fR" +Generate polymorphism-aware code. Specify this option if you use substitution +groups or +.BR xsi:type . +Use the +.B --polymorphic-type +option to specify which type hierarchies are polymorphic. + +.IP "\fB\--runtime-polymorphic\fR" +Generate non-polymorphic code that uses the runtime library configured with +polymorphism support. + +.IP "\fB\--polymorphic-type \fItype\fR" +Indicate that +.I type +is a root of a polymorphic type hierarchy. The compiler can often +automatically determine which types are polymorphic based on the +substitution group declarations. However, you may need to use this +option if you are not using substitution groups or if substitution +groups are defined in another schema. You need to specify this option +when compiling every schema file that references +.IR type . + +.IP "\fB\--generate-typeinfo\fR" +Generate custom type information querying functions for polymorphic +object model types. These functions can be used instead of the standard +C++ RTTI mechanism to determine object's type at runtime. + +.IP "\fB\--polymorphic-schema \fIfile\fR" +Indicate that +.I file +contains derivations of polymorphic types that are not otherwise visible +from the schema being compiled. This option is used to make sure that +during the generation of parser and serializer aggregates the compiler +is aware of all possible derivations of polymorphic types. Repeat this +option to specify more than one schema file. + .IP "\fB\--reuse-style-mixin\fR" Generate code that supports the mixin base parser/serializer implementation reuse style. Note that this reuse style diff --git a/documentation/xsde.xhtml b/documentation/xsde.xhtml index 312d5f9..c8f602a 100644 --- a/documentation/xsde.xhtml +++ b/documentation/xsde.xhtml @@ -870,6 +870,39 @@ Reset support allows you to reuse parsers and serializers after an error. +
    --generate-polymorphic
    +
    Generate polymorphism-aware code. Specify this option if you use + substitution groups or xsi:type. Use the + --polymorphic-type option to specify which + type hierarchies are polymorphic.
    + +
    --runtime-polymorphic
    +
    Generate non-polymorphic code that uses the runtime library + configured with polymorphism support.
    + +
    --polymorphic-type type
    +
    Indicate that type is a root of a polymorphic + type hierarchy. The compiler can often automatically determine + which types are polymorphic based on the substitution group + declarations. However, you may need to use this option if you are + not using substitution groups or if substitution groups are defined + in another schema. You need to specify this option when compiling + every schema file that references type.
    + +
    --generate-typeinfo
    +
    Generate custom type information querying functions for + polymorphic object model types. These functions can be used + instead of the standard C++ RTTI mechanism to determine + object's type at runtime.
    + +
    --polymorphic-schema file
    +
    Indicate that file contains derivations of + polymorphic types that are not otherwise visible from the schema + being compiled. This option is used to make sure that during the + generation of parser and serializer aggregates the compiler is + aware of all possible derivations of polymorphic types. Repeat + this option to specify more than one schema file.
    +
    --reuse-style-mixin
    Generate code that supports the mixin base parser/serializer implementation reuse style. Note that this reuse style @@ -877,7 +910,6 @@ object code size increase for large vocabularies. By default the tiein reuse style is used.
    -
    --custom-data type
    Add the ability to store custom data to the C++ class generated for XML Schema type type. To add custom -- cgit v1.1