Do Go with the Flow

Combining several renderer classes into a rendering cascade is a powerful technique for enabling maximum reuse of widgets in other contexts. However, this technique requires that the renderers conform to the expectations of the renderer API and the CDEJ that manages it rather than try to do things another way. Renderer classes should respect the imperative to render the data referenced by the paths in the Field object's binding without trying to examine what the paths represent of react differently to different kinds of paths. Any renderer class that implements special handling of paths or other information is likely to be unusable in all but the context for which it was first developed.

The key to going with the flow in a rendering cascade is to develop view-renderer and edit-renderer classes in a manner that makes them suitable for direct use in combination with a UIM FIELD element. This should be the case even for renderer classes that are never intended to be used directly in this way and only intended to be used in the context of a complex widget's rendering cascade. Making this the design goal ensures that the renderer class is context independent and will maximize the possibilities for its reuse.

When using XML document, it may be necessary to change the structure of the data to suit the rendering cascade. For example, a contact details widget is required to display the contact details of a person. The widget is expected, when complete, to provide reusable widgets that display the postal address and e-mail address of the person in the required form. The developer first conceives that the XML consumed by the new contact details widget will have the form shown below.

Figure 1. An XML Document Describing Contact Details
<contact>
  <name>James Smith</name>
  <street>Main Street</street>
  <city>Springfield</city>
  <phone>555-555-0101</phone>
  <e-mail>james@example.com</e-mail>
</contact>

The initially invoked renderer plug-in for the new widget, the contact renderer, will use the copy-and-modify technique on the Field object described in Overview of the Renderer Component Model and demonstrated in Tying Widgets Together in a Cascade and then delegate the rendering of these copied objects to the other renderers. To the address widget, the contact widget delegates a Field object whose source path is extended with /contact and the address widget will further extend this path with /street and /city to resolve and present the address values.

This arrangement will work, but the reusability of the address widget has been compromised by the order in which the paths have been extended. This is a consequence of the structure of the XML document. Were the address renderer to be used in a standalone address widget, its XML data might look like this:

Figure 2. An XML Document Describing an Address
<address>
  <street>Main Street</street>
  <city>Springfield</city>
</address>

The street and city elements are contained within an address element, as the XML document would not be valid without a single root element. This requires that the address renderer extend the source path (in this case just the path that identifies the server interface property itself) with /address/street and /address/city. These path extensions are not the same as those used with the address renderer was invoked by the contact renderer, so something is wrong.

This problem could be solved by having the contact renderer set a field parameter on the copy of the field passed to the address renderer instructing the renderer to extend the paths in different ways. This field parameter would not be set if the address renderer were invoked directly in correspondence with a UIM FIELD element, so the context could then be determined. However, this complicates both renderers in several ways. The contact renderer must accommodate the requirements of the address renderer to extend paths in one of two ways, the address renderer must check a field parameter value and then operate differently depending on the result. The XML is different for the address in each case, so any code that generates this XML would need to accommodate the requirements of the two renderers. Testing also becomes more difficult, as there are more paths through the code and more edge cases to consider. Therefore, this is not the right solution to the problem.

The alternative is much simpler: simply revise the structure of the XML document to the form shown below.

Figure 3. A Revised XML Document Describing Contact Details
<contact>
  <address>
    <street>Main Street</street>
    <city>Springfield</city>
  </address>
  <phone>555-555-0101</phone>
  <e-mail>james@example.com</e-mail>
</contact>

The address details are now embedded in the contact details XML document in the same form as they would appear in a standalone address XML document. As before, the contact renderer extends the path with /contact before delegating to the address renderer and then the address renderer extends that path further with /address/street and /address/city, just as it would do in the standalone use case. There is no need for any conditional processing and the need to deliver an address renderer that works in the context of a rendering cascade or when directly associated with a UIM FIELD element has not resulted in any added complication.

The situation for the e-mail address value is slightly different. In the standalone use case, the e-mail address renderer does not expect an XML document, just a string value containing the e-mail address. To accommodate this, the contact details renderer should extend the path for the e-mail address using /contact/e-mail before delegating the rendering of the value. Both renderers can now operate without any additional complication, as the e-mail address renderer will blindly resolve its source path to the e-mail address value and be unaffected by the fact that the path may either directly refer to a server interface property value or be extended to refer to a value within an XML document. In either case, the result of calling DataAccessor.get on the source path will be the string value of the e-mail address.

To design a rendering cascade that is effective in reusing renderers in a new context, proceed as follows:

Taking this bottom-up approach to the design will ensure that each of the ultimate elements in the rendering cascade are clearly defined and readily reusable. Taking a top-down approach may seem to work well at first, but it is almost inevitable that some problem will occur at the final level that results in the need to start the whole design again, as the design flaw cascades back in the opposite direction to the intended rendering cascade.