Derived Beans

What Is A Derived Bean?

A derived bean is one whose runtime class differs from the mapped property type (as determined during introspection).

Specifying The Bind Time Type

Using An Attribute To Specify Class

An easy way to specify the class to be loaded is for the class name to be specified by an xml attribute. By default, the className attribute (if present) is assumed to contain the name of the class to be loaded.

For example the following xml:

    <Animals> 
        <animal className='org.apache.commons.betwixt.io.read.FerretBean'> 
            <call>Dook</call>
            <colour>albino</colour> 
            <latinName>Mustela putoris furo</latinName>
            <name>Lector</name> 
        </animal> 
        <animal className='org.apache.commons.betwixt.io.read.CatBean'> 
            <call>Meow</call>
            <colour>black</colour> 
            <latinName>Felis catus</latinName>
            <name>Sam</name> 
        </animal> 
        <animal className='org.apache.commons.betwixt.io.read.DogBean'> 
            <breed>mongrol</breed>
            <call>Woof</call>
            <latinName>Canis familiaris</latinName> 
            <name>Bobby</name>
            <pedigree>false</pedigree> 
        </animal> 
    </Animals>

will construct objects of type FerretBean, CatBean and DogBean.

The attribute which contains the fully qualified class name can be configured through the classNameAttribute property of the BindingConfiguration.

The classname mapping is implemented by a ChainedBeanCreator. If this feature is not required, a custom bean creation chain should be used.

Mapping An Element To A Class Name

A common variation on the above occurs when the java and the xml should be decoupled. So, no attributes with class names permitted. A common use case requires that different elements are associated with different types of bean. For example:

    <animals> 
        <ferret> 
            <call>Dook</call>
            <colour>albino</colour> 
            <latinName>Mustela putoris furo</latinName>
            <name>Lector</name> 
        </ferret> 
        <cat> 
            <call>Meow</call>
            <colour>black</colour> 
            <latinName>Felis catus</latinName>
            <name>Sam</name> 
        </cat> 
        <dog> 
            <breed>mongrol</breed>
            <call>Woof</call>
            <latinName>Canis familiaris</latinName> 
            <name>Bobby</name>
            <pedigree>false</pedigree> 
        </dog> 
    </animals>

To read this xml, the class corresponding to each element must be known. Of course, this is a mixed collection and so needs to be mapped as such. Then a suitable polymophoric mapping needs to be created. For example:

<betwixt-config>
  <class name='org.apache.commons.betwixt.io.read.Animals'>
    <element name='animals'>
<!--
Note that an adder must be set but addDefaults will add a name
(and so prevent a polymorphic mapping) and so should be avoided.
-->
      <element property='animals' updater='addAnimal'/>
    </element>
  </class>
  <class name='org.apache.commons.betwixt.io.read.FerretBean'>
    <element name='ferret'>
      <addDefaults/>
    </element>
  </class>
  <class name='org.apache.commons.betwixt.io.read.CatBean'>
    <element name='cat'>
      <addDefaults/>
    </element>
  </class>
  <class name='org.apache.commons.betwixt.io.read.DogBean'>
    <element name='dog'>
      <addDefaults/>
    </element>
  </class>
</betwixt-config>

This multi mapping should be registered with the XMLIntrospector before the beans are written or read. For example (read):

   BeanReader reader = new BeanReader();
   ...
   reader.getXMLIntrospector().register(new InputSource(new StringReader(MAPPING)));
   reader.registerBeanClass(Animals.class);
   Animals animals = (Animals) reader.parse(in);

Mapping Derived Beans

Bind Time Type Verses Introspection Time Type

The introspection time type is the (expected) type as discovered during introspection. The bind time type is the actual type of the object found as Betwixt runs. For example, a property might have type Animal but when the getter of that property is called, the object returned might be a subclass (if Animal is a class) or an implementation (if Animal is an interface) - Cat, say. In this case, Animal would be the introspection time type but Cat the bind time type.

There are two approaches to deal with this situation: either the object can be mapped on the basis of it's bind time type (the actual class of the object) or on the basis of it's introspection time type. In the first case, all the properties present will be mapped, in the second only a subset.

For example, say that Animal has properties latinName and call and that Cat is a subclass with an additional colour property. Consider a bean with a property returning an Animal. If the introspection time type is used, only the latinName and call properties of aCat bean will be read and written whereas when bind time typing is used, all properties will be.

There are typical use cases which demonstrate the need for each approach. Betwixt supports both in a flexible way, for both reading and writing but defaults to bind time typing (which is typically more intuitive).

Configuring Mapping Derivation

Whether the bind time type or the introspection time type should determine the mapping is configurable in two different ways. The MappingDerivationStrategy implementation set on the introspection configuration allows powerful but course-grained control over which types of property should have their mappings determine in which ways. Per-element control is also available through the dot betwixt document. This is typically used for more finely grained refinements. The dot betwixt settings override the strategy.

For example, the following code snippet configures an XMLIntrospector so that (unless overridden by a dot betwixt document setting), introspection time types will determine the mapping:

        XMLIntrospector xmlIntrospector = ...;
        xmlIntrospector.getConfiguration()
            .setMappingDerivationStrategy(
                MappingDerivationStrategy.USE_INTROSPECTION_TIME_TYPE);
}	

The optional mappingDerivation attribute on an <element> element. This attribute takes two enumerated values: bind and introspection. As might be expected, bind indicates that the element should use the bind time type whereas introspection indicates that the introspection time type should be used.

For example, when the following dot betwixt document is used, the pet property's mapping will be determined by the introspection time type mapping.

    <?xml version='1.0'?>
    <info>
        <element name='pet-record'>
            <element name='pet' property='pet' mappingDerivation='introspection'/>" +
        </element>
    </info>"
}	

Note that the mapping derivations are added together with all of the other defaults and so all elements without the mappingDerivation attribute will default to bind time typing unless and addDefaults is present.

Default Mapping For Collections Implementations

The default mapping for a collection is the one created automatically by Betwixt. This is controlled by a number of general configuration settings. For some classes (including Collection implementations) special rules are applied. For collection implementations, these special rules ensure that the contents are written and polymorphism is supported for reading.

For example, suppose AlphaList extends ArrayList. It usually contains BetaBean's and GammaBean's. Use a multi-mapping contain mappings for just BetaBean and GammaBean and not AlphaList. This will force the default mapping to be used. When reading, AlphaList should be registered after the multimapping thus:

	BeanReader reader = ...
	...
	reader.registerMultiMapping(...);
    reader.registerBeanClass(AlphaList.class);