35.2. Substitution Groups in Java

Overview

Apache CXF, as specified in the JAXB specification, supports substitution groups using Java's native class hierarchy in combination with the ability of the JAXBElement class' support for wildcard definitions. Because the members of a substitution group must all share a common base type, the classes generated to support the elements' types also share a common base type. In addition, Apache CXF maps instances of the head element to JAXBElement<? extends T> properties.

Generated object factory methods

The object factory generated to support a package containing a substitution group has methods for each of the elements in the substitution group. For each of the members of the substitution group, except for the head element, the @XmlElementDecl annotation decorating the object factory method includes two additional properties, as described in Table 35.1, “Properties for Declaring a JAXB Element is a Member of a Substitution Group”.

Table 35.1. Properties for Declaring a JAXB Element is a Member of a Substitution Group

PropertyDescription
substitutionHeadNamespaceSpecifies the namespace where the head element is defined.
substitutionHeadNameSpecifies the value of the head element's name attribute.
The object factory method for the head element of the substitution group's @XmlElementDecl contains only the default namespace property and the default name property.
In addition to the element instantiation methods, the object factory contains a method for instantiating an object representing the head element. If the members of the substitution group are all of complex types, the object factory also contains methods for instantiating instances of each complex type used.

Example 35.5. Object Factory Method for a Substitution Group

public class ObjectFactory {

    private final static QName _Widget_QNAME = new QName(...);
    private final static QName _PlasticWidget_QNAME = new QName(...);
    private final static QName _WoodWidget_QNAME = new QName(...);

    public ObjectFactory() {
    }

    public WidgetType createWidgetType() {
        return new WidgetType();
    }

    public PlasticWidgetType createPlasticWidgetType() {
        return new PlasticWidgetType();
    }

    public WoodWidgetType createWoodWidgetType() {
        return new WoodWidgetType();
    }

    @XmlElementDecl(namespace="...", name = "widget")
    public JAXBElement<WidgetType> createWidget(WidgetType value) {
        return new JAXBElement<WidgetType>(_Widget_QNAME, WidgetType.class, null, value);
    }

    @XmlElementDecl(namespace = "...", name = "plasticWidget", substitutionHeadNamespace = "...", substitutionHeadName = "widget")
    public JAXBElement<PlasticWidgetType> createPlasticWidget(PlasticWidgetType value) {
        return new JAXBElement<PlasticWidgetType>(_PlasticWidget_QNAME, PlasticWidgetType.class, null, value);
    }

    @XmlElementDecl(namespace = "...", name = "woodWidget", substitutionHeadNamespace = "...", substitutionHeadName = "widget")
    public JAXBElement<WoodWidgetType> createWoodWidget(WoodWidgetType value) {
        return new JAXBElement<WoodWidgetType>(_WoodWidget_QNAME, WoodWidgetType.class, null, value);
    }

}

Substitution groups in interfaces

If the head element of a substitution group is used as a message part in one of an operation's messages, the resulting method parameter will be an object of the class generated to support that element. It will not necessarily be an instance of the JAXBElement<? extends T> class. The runtime relies on Java's native type hierarchy to support the type substitution, and Java will catch any attempts to use unsupported types.
To ensure that the runtime knows all of the classes needed to support the element substitution, the SEI is decorated with the @XmlSeeAlso annotation. This annotation specifies a list of classes required by the runtime for marshalling. Fore more information on using the @XmlSeeAlso annotation see Section 30.4, “Adding Classes to the Runtime Marshaller”.

Example 35.6. WSDL Interface Using a Substitution Group

<message name="widgetMessage">
    <part name="widgetPart" element="xsd1:widget" />
  </message>
  <message name="numWidgets">
    <part name="numInventory" type="xsd:int" />
  </message>
  <message name="badSize">
    <part name="numInventory" type="xsd:int" />
  </message>
  <portType name="orderWidgets">
    <operation name="placeWidgetOrder">
      <input message="tns:widgetOrder" name="order" />
      <output message="tns:widgetOrderBill" name="bill" />
      <fault message="tns:badSize" name="sizeFault" />
    </operation>
    <operation name="checkWidgets">
      <input message="tns:widgetMessage" name="request" />
      <output message="tns:numWidgets" name="response" />
    </operation>
  </portType>

Example 35.7. Generated Interface Using a Substitution Group

@WebService(targetNamespace = "...", name = "orderWidgets")
@XmlSeeAlso({com.widgetvendor.types.widgettypes.ObjectFactory.class})
public interface OrderWidgets {

    @SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
    @WebResult(name = "numInventory", targetNamespace = "", partName = "numInventory")
    @WebMethod
    public int checkWidgets(
        @WebParam(partName = "widgetPart", name = "widget", targetNamespace = "...")
        com.widgetvendor.types.widgettypes.WidgetType widgetPart
    );
}
Tip
The SEI shown in Example 35.7, “Generated Interface Using a Substitution Group” lists the object factory in the @XmlSeeAlso annotation. Listing the object factory for a namespace provides access to all of the generated classes for that namespace.

Substitution groups in complex types

When the head element of a substitution group is used as an element in a complex type, the code generator maps the element to a JAXBElement<? extends T> property. It does not map it to a property containing an instance of the generated class generated to support the substitution group.
For example, the complex type defined in Example 35.8, “Complex Type Using a Substitution Group” results in the Java class shown in Example 35.9, “Java Class for a Complex Type Using a Substitution Group”. The complex type uses the substitution group defined in Example 35.2, “Substitution Group with Complex Types”.

Example 35.8. Complex Type Using a Substitution Group

<complexType name="widgetOrderInfo">
  <sequence>
    <element name="amount" type="xsd:int"/>
    <element ref="xsd1:widget"/>
  </sequence>
</complexType>

Example 35.9. Java Class for a Complex Type Using a Substitution Group

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "widgetOrderInfo", propOrder = {"amount","widget",})
public class WidgetOrderInfo {

    protected int amount;
    @XmlElementRef(name = "widget", namespace = "...", type = JAXBElement.class)
    protected JAXBElement<? extends WidgetType> widget;
    public int getAmount() {
        return amount;
    }

    public void setAmount(int value) {
        this.amount = value;
    }

    public JAXBElement<? extends WidgetType> getWidget() {
        return widget;
    }

    public void setWidget(JAXBElement<? extends WidgetType> value) {
        this.widget = ((JAXBElement<? extends WidgetType> ) value);
    }

}

Setting a substitution group property

How you work with a substitution group depends on whether the code generator mapped the group to a straight Java class or to a JAXBElement<? extends T> class. When the element is simply mapped to an object of the generated value class, you work with the object the same way you work with other Java objects that are part of a type hierarchy. You can substitute any of the subclasses for the parent class. You can inspect the object to determine its exact class, and cast it appropriately.
Tip
The JAXB specification recommends that you use the object factory methods for instantiating objects of the generated classes.
When the code generators create a JAXBElement<? extends T> object to hold instances of a substitution group, you must wrap the element's value in a JAXBElement<? extends T> object. The best method to do this is to use the element creation methods provided by the object factory. They provide an easy means for creating an element based on its value.
Example 35.10, “Setting a Member of a Substitution Group” shows code for setting an instance of a substitution group.

Example 35.10. Setting a Member of a Substitution Group

ObjectFactory of = new ObjectFactory(); 1
PlasticWidgetType pWidget = of.createPlasticWidgetType(); 2
pWidget.setShape = "round';
pWidget.setColor = "green";
pWidget.setMoldProcess = "injection";

JAXBElement<PlasticWidgetType> widget = of.createPlasticWidget(pWidget); 3

WidgetOrderInfo order = of.createWidgetOrderInfo(); 4
order.setWidget(widget); 5
1
Instantiates an object factory.
2
Instantiates a PlasticWidgetType object.
3
Instantiates a JAXBElement<PlasticWidgetType> object to hold a plastic widget element.
4
Instantiates a WidgetOrderInfo object.
5
Sets the WidgetOrderInfo object's widget to the JAXBElement object holding the plastic widget element.

Getting the value of a substitution group property

The object factory methods do not help when extracting the element's value from a JAXBElement<? extends T> object. You must to use the JAXBElement<? extends T> object's getValue() method. The following options determine the type of object returned by the getValue() method:
  • Use the isInstance() method of all the possible classes to determine the class of the element's value object.
  • Use the JAXBElement<? extends T> object's getName() method to determine the element's name.
    The getName() method returns a QName. Using the local name of the element, you can determine the proper class for the value object.
  • Use the JAXBElement<? extends T> object's getDeclaredType() method to determine the class of the value object.
    The getDeclaredType() method returns the Class object of the element's value object.
    Warning
    There is a possibility that the getDeclaredType() method will return the base class for the head element regardless of the actual class of the value object.
Example 35.11, “Getting the Value of a Member of the Substitution Group” shows code retrieving the value from a substitution group. To determine the proper class of the element's value object the example uses the element's getName() method.

Example 35.11. Getting the Value of a Member of the Substitution Group

String elementName = order.getWidget().getName().getLocalPart();
if (elementName.equals("woodWidget")
{
  WoodWidgetType widget=order.getWidget().getValue();
}
else if (elementName.equals("plasticWidget")
{
  PlasticWidgetType widget=order.getWidget().getValue();
}
else
{
  WidgetType widget=order.getWidget().getValue();
}