xml-xmlbeans-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From xmlbeans-...@xml.apache.org
Subject [Apache XMLBeans Wiki] New: SubstGroupsFaq
Date Thu, 19 Aug 2004 22:08:15 GMT
   Date: 2004-08-19T15:08:15
   Editor: RaduPreotiuc <radup@bea.com>
   Wiki: Apache XMLBeans Wiki
   Page: SubstGroupsFaq
   URL: http://wiki.apache.org/xmlbeans/SubstGroupsFaq

   no comment

New Page:

#pragma section-numbers off

= Substitution Groups FAQ =

 * [#introduction Introduction. What are substitution groups?]
 * [#whyUse Why would one use substitution groups?]
 * [#whyHard Why is it so hard to work with substitution groups from Java?]
 * [#xmlObject How does XmlObject work with types/elements?]
 * [#sGroupsReadOnly How do I use substitution groups in a read-only scenario?]
 * [#sGroupsWrite How do I use substitution groups in write scenarios?]
 * [#sGroupsWrite1 How do I use substitution groups in a bottom-up write scenario (using the
"setXXX()" methods)?]
 * [#sGroupsWrite2 How do I use substitution groups in a top-down write scenario (using the
"addNewXXX()" methods)?]
 * [#whyNotMethods Why doesn't XmlBeans generate additional methods for every possible substitution,
to support substitution groups?]
 * [#whyNotAutomatic Why doesn't XmlBeans figure out automatically when I call "setXXX()"
which substitution group it should use?]
 * [#whatsThePlan What does XmlBeans plan to do to improve the support for substitution groups?]
 * [#newIdea I have a new idea on how substitution groups could be better supported. What
do I do?]

'''What is the latest version of XMLBeans?'''
'''Introduction. What are substitution groups?'''

Substitution groups in XMLSchema extend the concept of type substitution (a.k.a. polymorphism)
to XML elements.

{{{<xs:element name="a" type="type"/>
<xs:element name="b" type="derivedtype" substitutionGroup="a"/>}}}

This XMLSchema snippet declares two elements, named "a" and "b", and moreover specifies that
"b" is in the substitution group headed by "a". This makes it possible to use "b" in every
place that "a" would be valid in an instance document.
is a valid instance document, then so is

Note that in order for the Schema definitions to be valid, the type "derivedtype" must be
derived from "type" using XMLSchema type derivation, like in:

{{{<xs:type name="type">
  <!-- Definition for type "type" -->
<xs:type name="derivedtype">
    <xs:extension base="type">
      <!-- Definition for the type "derivedtype" -->

(one can of course use <xs:restriction> in place of <xs:extension>)

'''Why would one use substitution groups?'''

In general, there is no reason to, especially in data-oriented environments. If you use XML
to encode and pass data around, there is nothing that you can achieve by using substitution
groups and you can't by using normal type substitution (xsi:type).

'''Why is it so hard to work with substitution groups from Java?'''

Basically the main problem arises from the fact that while XMLSchema types map almost naturally
to Java types, XMLSchema elements (and attributes) only map to !JavaBeans-style properties,
which don't have polymorphism. To finish drawing the parallel, it would be like being able
to say in Java "I want to use calls to method x() instead of calls to method y() on every
object on which y() is a legal method call". That would really be confusing, now, wouldn't
Since this mismatch exists, it's challenging to come up with a good translation of this concept
into Java.

'''How does XmlObject work with types/elements?'''

This does not relate to substitution groups in particular, but it is relevant to note that
!XmlObjects model Schema types, which is the natural mapping and not Schema elements. The
difference is that a Schema element is a container, while a Schema type is a description of
the contents of a container (that's why you can have an element and an attribute both having
the same type). Therefore, an instance of an XmlObject (typed or not) represents the <b>contents</b>
of a container (element or attribute) and not the container itself. As such, when you do {{{MyXmlObject.Factory.newInstance()}}}
you are creating the contents of a generic XML element, waiting to be assigned, using a "setXXX()"
method, somewhere in your XML document.

'''How do I use substitution groups in a read-only scenario?'''

Let's say you have declared an element named "a" and an element "b" in the substitution group
headed by "a", like in the introduction. Now let's assume the following input document:

Then you can do (assuming normal generated !XmlBeans):

{{{Type[] contents = container.getAArray();
for (int i = 0; i < contents.length; i++)
  if (contents[i].schemaType() == Type.type)
    // do specific processing for the type "type"
  if (contents[i].schemaType() == Derivedtype.type)
    // do processing specific to "derivedtype"
That's all good but you can achieve the same processing semantics using the following XML

  <a xsi:type="derivedtype"/>

If you want to know whether substitution groups were used or not, your code would have to
look like

{{{Type[] contents = container.getAArray();
QName b = new QName("", "b");
for (int i = 0; i  contents.length; i++)
  if (b.equals(contents[i].newCursor().getName()))
    // a substitution group was used!
'''How do I use substitution groups in write scenarios?'''

This is where is gets dicey ;-) See one of the following questions.

'''How do I use substitution groups in a bottom-up write scenario (using the "setXXX()" methods)?'''

Here's how to use it, given the Schema definition from Introduction.

{{{ContainerDocument.Container c = ...;
Derivedtype d = ...;
c.setAArray(1, c);  // This sets up the contents, since it is an XmlObject assignment
c.getAArray(1).newCursor().changeName(new QName("", "b")); // This sets up the element name,
for substitution groups}}}

Note that, since the setter is called "setAArray", the container's element name would be "a"
and the type would be what's passed in, consistent with the rule that elements are mapped
to !JavaBeans properties and types to !XmlObjects.

'''How do I use substitution groups in a top-down write scenario (using the "addNewXXX()"

Given the Schema definitions from Introduction, the following code snippet would do it:

{{{ContainerDocument.Container c = ...;
Type t = c.addNewA();
t.newCursor().setName(new QName("", "b")); // This sets up the substituted name
Derivedtype d = (Derivedtype) c.getAArray(1); // This retrieves the XmlObject of the new type,
on which sets can be called}}}

Note that this approach too requires the use of "newCursor().setName()" and involves getting
back from the parent an object of the new, derived type.

'''Why doesn't !XmlBeans generate additional methods for every possible substitution, to support
substitution groups?'''

This has to do with what kind of information is discoverable at compile-time.
Suppose that we change the example provided in "Introduction" such that elements "a" (the
substitution group head) and "b" (the substitution group member) are defined in different
files. You can either compile both files in a single compilation step, or you can compile
the file containing "a" first, and then the file containing "b" (if "a" would be useful in
its own right, for example). When !XmlBeans generates the setter for the uses of "a" (first
compilation) it does not yet know that there is a substitution group involved, so no extra
methods are generated. When it compiles "b" (second compilation), it notices that a substitution
group for "a" is defined, but it's too late to change "a", since it's classed have already
been generated and may be in use somewhere else. This contrasts with the scenario where both
files are compiled at the same time, where additional methods could be generated.
In summary, the generated methods would depend upon the order in which the source Schema documents
were compiled, which would be strange at the very least. In Java, the contents of a compiled
.class do not depend on what other sources were compiled at the same time, so it would not
feel right to do that in Schema.
And still the case in which the file containing "a" was compiled first would not be solved.
So, we would have an anomaly in behavior that only helps solving half of the problem; better
without it.

'''Why doesn't !XmlBeans figure out automatically when I call "setXXX()" which substitution
group it should use?'''

That is a good question, but there is a good answer for it too.
First of all, when calling a setter and passing in an XmlObject of a specific type, it is
very possible to use type substitution instead of substitution groups (i.e. use xsi:type)
to achieve the same effect and a valid document. So there will need to be an option telling
!XmlBeans "use substitution groups whenever possible". Even then, there could exist multiple
elements having the same type. So fine, !XmlBeans should pick whatever element in the substitution
group of the declared element having as type the run-time type that has been passed in to
Unfortunately, the question "find me all elements in a given substitution group" is not answerable
at run-time. Because one can always drop additional jars on the classpath containing new types
and elements, some of these elements can augment the existing substitution groups. Since a
Java !ClassLoader can answer questions like "find me resource x", but can't answer questions
like "enumerate all resources of this kind", one can never build a list of all available !SchemaTypes
(or Elements) that have a certain property. I think that the convenience and consistency of
!SchemaTypes behaving like !JavaTypes with respect to classloading and compile-time vs. run-time
behavior far outweighs the minor inconvenience of having to write one extra line of code when
using substitution groups :-)
Note. There is the case of anonymous types, which don't have the two properties discussed
above: they don't work with type substitution and they can only be contained in a single element,
to which they are local. So when anonymous types are used, one could generate the substitution
needed, but then that would contrast to the case where types are not anonymous, so it's not
at all clear that it would be a good idea to default to substitution groups when anonymous
derived types are used.

'''What does !XmlBeans plan to do to improve the support for substitution groups?'''

The current plan is to add a method to XmlObject similar to
    !XmlObject changeType(!XmlObject newType)
The new method would be
    !XmlObject substitute(!XmlObject newType, !QName newName)
and would, assuming a certain strict set of constraints on parameters and on the instance
that it's called on is met, return a new XmlObject whose run-time type is the same as newType's
(similar up to this point to what .changeType() is doing) and which is hooked up to its container
using the QName that's being passed in.
This method would be nice in that it eliminates the need to retrieve the XmlObject using a
get before it can be used, allowing code like (assuming the Schema definitions presented in

{{{ContainerDocument.Container c = ...;
Type t = c.addNewA();
Derivedtype d = (Derivedtype) t.substitute(Derivedtype.type, new QName("", "b"));}}}

This will work in all cases (legal cases of course) and it's pretty simple to use IMHO.

'''I have a new idea on how substitution groups could be better supported. What do I do?'''

Feedback is always welcome, so don't hesitate to present your ideas and engage in discussions
on the xmlbeans dev forum, at xmlbeans-dev@xml.apache.org


To unsubscribe, e-mail: xmlbeans-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: xmlbeans-cvs-help@xml.apache.org

View raw message