Constructor based rule

One of the missing features of the old Digester releases is that the ObjectCreateRule works just with the default empty constructor.

One limit that cannot be exceeded is the fact that constructor arguments cannot be extracted from inner XML elements; that's because the ObjectCreateRule creates the object when the related XML element begins, otherwise properties could not be set when parsing nested elements.

That is no longer true :) Constructor arguments can be extracted from attributes and nested elements of the matching XML element for whom the ObjectCreateRule is triggered.

NOTE this feature is available since release 3.2.

Using plain old Digester APIs

ObjectCreateRule has new APIs to configure the constructor arguments types; given for example the XML snippet below:

<root>
  <bean super="true">
    <rate>9.99<rate/>
  </bean>
</root>

That has to be mapped to the bean:

class MyBean
{

    public MyBean( Double rate, Boolean super )
    {
        ...
    }

}

Then the Digester instance can be configured as below:

ObjectCreateRule createRule = new ObjectCreateRule( MyBean.class );
createRule.setConstructorArgumentTypes( boolean.class, double.class );

Digester digester = new Digester();
digester.addRule( "root/bean", createRule );
digester.addCallParam( "root/bean", 1, "super" );
digester.addCallParam( "root/bean/rate", 0 );

Using the RulesBinder APIs

The Binder APIs just allow expressing the same rule in a fluent way:

DigesterLoader loader = ( new AbstractRulesModule()
{

    @Override
    protected void configure()
    {
        forPattern( "root/bean" )
            .createObject().ofType( MyBean.class ).usingConstructor( boolean.class, double.class )
            .then()
            .callParam().fromAttribute( "super" ).ofIndex( 1 );
        forPattern( "root/bean/rate" ).callParam().ofIndex( 0 );
    }

} );

Using the annotations

Since 3.2, ObjectCreate can be used to annotate constructors as well; with the introduction of Attribute annotation, constructor based rules can be expressed like:

class MyBean
{

    @ObjectCreate( pattern = "root/bean" )
    public MyBean( @CallParam( pattern = "root/bean/rate" ) Double rate,
                   @CallParam( pattern = "root/bean", attributeName = "super" ) Boolean super )
    {
        ...
    }

}

Using the XML meta-descriptor

The XML ruleset supports as well the new constructor rule, <object-create-rule> supports a new inner element <constructor-argument>:

<digester-rules>
  <pattern value="root/bean">
    <object-create-rule classname="MyBean
          paramtypes="java.lang.Double,java.lang.Boolean">
      <constructor-argument attrname="rate" type="java.lang.Double" />
      <call-param-rule paramnumber="0" pattern="rate" />
      <call-param-rule paramnumber="1" attrname="super" />
    </object-create-rule>
  </pattern>
</digester-rules>

Default constructor arguments

In order to provide that feature, Digester relies on CGLIB that needs to proxy the class of the target object, to avoid constructors issues users can use the ObjectCreateRule#setDefaultConstructorArguments(Object...) method to give safe construction params to the constructor when creating the proxy; that method could be useful also when one parameter needs to take a static value while the other is handled with a CallParam rule. If these are not specified, the super constructor is called with nulls for Objects and default values for primitives.