Registration of Custom Data Bindings

Luan O'Carroll, XUI 3.1, 8th July 2007

Since XUI supports an open ended number of compenent types and a large variety of data objects it is often useful to create reusable data bindings for these situations. Prior to version 3.0 it was possible to add custom data bindings, but the mechanism employed suffered from some limitations. Therefore, to deal with these limitations and to provide a more consistent regsitration mechanism the data bindings registration mechanism has been completely revised.

Adding new binding types

The data binding mechanism in Carousel shares some common features with the other configuration file systems in Carousel and XUI. Two sets of configuration information are available when using bindings, firstly the binding types are registered and then the binding are instantiated in the context of the page.

The binding registration contains information that is used to control how and when the binding is instantiated. Mostly this information is used to match a particular binding to a particular component of to a particular type of component. The registration information can, in some cases, be used to further configure the binding instance or load adapter classes that customize the data binding's behavior.

The instantiation information is provided when the binding is processed and is passed to the new binding instance through a standard interface.

The system allows multiple configuration files to be added to an application so that various modules can contribute support to an application. The registration facility includes an iterative process for searching and matching bindings, starting with the most specific bindings specifications to the most general. The matching strategy is itself customizable so very specific matching can be used.

Adding a configuration file

The first step in employing the data bindings is to add a registration file for the bindings. By default XUI includes and adds a bindings.xml file for the built in bindings, and it is instructive to look at this file:

 
 
Register the binding types
 
 

<Bindings>
  <InspectorBindings>
  </InspectorBindings>
  
  <ClassBindings>
    <Binding target="net.xoetrope.*.XButton" 
      class="net.xoetrope.xui.data.XTextBinding" type=""/>
    <Binding target="net.xoetrope.swing.XRadioButton" 
      class="net.xoetrope.xui.data.XRadioBinding" type=""/>
    <Binding target="net.xoetrope.swing.XRadioButton" 
      class="net.xoetrope.xui.data.XTextBinding" type="text"/>
    <Binding target="net.xoetrope.swing.XRadioButton" 
      class="net.xoetrope.xui.data.XStateBinding" type="state"/>

    <Binding target="net.xoetrope.swing.XCheckbox" 
      class="net.xoetrope.xui.data.XStateBinding" type=""/>
    <Binding target="net.xoetrope.swing.XCheckbox" 
      class="net.xoetrope.xui.data.XTextBinding" type="text"/>
	<Binding target="net.xoetrope.swing.XTree" 
    class="net.xoetrope.swing.tree.XTreeBinding" type=""/>
    
    <!-- TODO add implementation of this error handling mechanism-->
    <!-- Binding target="net.xoetrope.*.XTable">
      <onError fixError="DataSourceClassMissing"/>
    </Binding-->
  </ClassBindings>
  
  <InterfaceBindings>
    <Binding target="net.xoetrope.xui.XListHolder" 
      class="net.xoetrope.xui.data.XListBinding" type=""/>
    <Binding target="net.xoetrope.xui.XListHolder" 
      class="net.xoetrope.xui.data.XListBinding" type=""/>
    <Binding target="net.xoetrope.xui.XTextHolder" 
      class="net.xoetrope.xui.data.XTextBinding" type=""/>

    <!-- TODO add implementation of this error handling mechanism-->
    <!-- This does not actually use a binding -->
    <!--Binding target="net.xoetrope.xui.XModelHolder">
      <onError fixError="DataSourceClassMissing"/>
    </Binding-->
  </InterfaceBindings>
  
  <InstanceBindings>
  </InstanceBindings>
</Bindings>
 
 

An application can add an application specific binding by including a bindings.xml file in its classpath. The name of the application bindings file can be changed by specifying the BindingsRegistry startup parameter.

An individual module or application can also add as many configuration files as it needs. For the data bindings the configurations are added to the XRegisteredDataBindingFactory via the addConfigFile method.

The configuration file format

The matching strategies used with the configuration files allow various levels of refinement so that a broad concept like an interface can be matched or a very specific class instance can also be matched. The matching modes are as follows

1 InspectorBindings uses a custom/user defined XInspector class to identify the appropriate binding. The inspector must implement the Xinspector interface via the 'inspector' attribute. 2 InstanceBindings match a class to an instance of the specified class 3 InterfaceBindings matches to a class that implements the specified interface 4 ClassBindings match a binding target to a instance of the specified class, or a subclass of the specified classs

each of these binding types should specify the target, class and type attributes, but the registration may also specify addition attributes and these are available to the data binding once the XDataBinding.setup method is invoked.

Within a particular mode of bindings the bindings registrations are checked in the order in which they are registered and specified.

Specifying an InspectorBinding

As mentioned above the Inspector Bindings allow you to create bindings that can inspect the comtents and type of the source data and the type and instance of the target component. Using a special XInspector interface you can create your own inspector and thereby gain fine grained control of how a binding is used and applied.

 
 
The XInspector interface
 
 
  /**
   * Called by the registration factory for XInspectorRegistration instances
   * to allow this matcher to arbitarate as to whether or not the component
   * can be used with the particular registration object.
   * @param comp the component involved in the drag oepration
   * @param regConfig a table of registration attributes
   * @param instConfig a table of attributes used in the instance 
   * declaration
   * @return true if the handler matches, otehrwise false
   */
  public boolean inspect( Object comp, Hashtable regConfig, 
                          Hashtable instConfig );
 
 

The structure of a data binding

Once a data binding has been selected it is instantiated through reflection and the setup method is called. The setup method provides an initial opportunity to make use of the registration and the instance parameters (from the page where the binding is used). The setup method takes the following signature

 
 
The binding setup interface
 
 
  /**
   * Setup and configure the binding instance. The binding is configured 
   * via the XML setup registered for the particular binding type and 
   * then, subsequently by attibitional attributes of the binding instance 
   * specified in the page declaration, for the individual binding instance. 
   * The binding may also obtain configuration or reference information 
   * from the component and the project.
   * @param project the owning project
   * @param c the component being bound
   * @param regConfig the XML element which contains the binding 
   * configuration
   * @param instanceConfig the XML element which contains the setup 
   * attributes
   * of the binding instance
   */
  public abstract void setup( XProject project, 
                              Object c, 
                              Hashtable regConfig, 
                              Hashtable instanceConfig )
 
 

Most bindings won't do very much with the parameters and instead rely on the setupHelper method to set the source and output data models to their initial state. An example of this is the XTextBinding

 
 
XTextBinding example
 
 
  public void setup( XProject project, 
                      Object c, 
                      Hashtable bindingConfig, 
                      Hashtable instanceConfig )
  {
    setupHelper( project, c, bindingConfig, instanceConfig );
    attribStr = (String)instanceConfig.get( "attrib" );
  }
 
 

Data binding adapter

Many data models nodes are not immediately usable in a binding, lacking the the form, structure, or the interface needed by the binding. An example of this is the XlistBinding which uses the XListModelAdapter to adapt data source such as tables to lists. A list may display just one column of a table or a single attribute of a model node and the adapter adapts the model node to the requirements of user component. Various adapters are built into the XUI framework. The adapter is specified with the 'adapter' attribute of the binding instance for those bidning types that use adapters.

Data binding lifecycle

Once the data binding registration has been matched, an instance of that binding is created and the setup method is called is as described above. The setup method usually establishes all the data and resources need by the binding.

Data binding contexts and containers

The binding is maintained within the XDataBindingContext owned by the page. As the page state changes with page navigation the data binding context requests that the binding saves its data by calling the set method and when the page is displayed the context calls the get method, requesting that the binding updates the UI component.

The application may also explicitly update the individual bindings using the updateBinding method, or via the updateBindings or updateBoundComponentValues methods.

Reevaluation of data bindings

The updateBindings method differs from the updateBoundComponentValues method in that it causes a reevaluation of the binding source and output model specification. The reevaluation is often used if the model node specified hass changed, or if the binding paths include evaluated attributes.

Evaluated attributes are references to methods of the owning page (or sometimes library objects), that allow applications to control and manipulate the model paths used by the bindings. In this way the application can redirect the bindings to different parts of the model. Sometimes this redirection is used to do thing like adapting a page to the data of one object or another, for example the data belonging to one user or another.

One important aspect of the registration facility is that the basic data bindings is that the bindings do not store the tables of binding instance and binding registration attributes, so if these attributes are needed by a binding then it is the responsibility of the individual binding class to store the necessary data.