Writing the Code
The downloadable code
for the generic transform that accompanies this article resides in a single XSLT file, although it might be easier to maintain if divided into several separate stylesheets. The stylesheet is divided into several logical areas.
- Area 1 copies the XML input into the XForms Model
- Area 2 implements the form namespace
- Area 3 performs the actual drawing of the input elements
- Area 4 contains variables and parameters to configure the solution
Finally, there's some code that wraps these four parts and lets them work together.
The rest of this section describes the areas sufficiently to let you modify the XSLT to meet your own requirements so you can produce functioning forms.
The code that wraps the other four parts together is basically some boilerplate for generating ODF documents. It generates the document element (office:document-content
), interweaves the style sections, and then builds the basic office body, as shown in the code fragment below, which comes from the main template matching the root node.
<xsl:apply-templates select="$doc-element" mode="XFORMS"/>
<xsl:apply-templates select="$doc-element" mode="FORMS"/>
<xsl:apply-templates select="$doc-element" mode="oobuilder"/>
In the preceding code, you can see the three main modes: XFORMS
, which builds everything within the XFORMS model, FORMS
, which builds the elements in the forms namespace, and oobuilder
, which assembles the actual visible office content. The doc-element
parameter being processed represents the document element of the input document; the tree needs to be processed three different times to generate the XFORMS
namespace section, the FORMS
namespace section, and the actual document content.
In each case, the stylesheet processes the top level of the input (represented by the XPath /*
), and then processes all other elements and attributes, represented by templates matching the XPaths *
respectively. Because XForms do not support mixed content models, and because bindings must be generated only for content that does not have descendant nodes, each template matching an element will have an if
statement that outputs content on its level only when there are no child elements.
shows a template that matches any element node except the document element.
Basically, this template takes some input parameters used to keep track of where the element is in the document order by building an XPath representing the current element. You could probably improve the code according to your taste; for example, many people might prefer to use the current()
function to retrieve values from the current node rather than placing those values into variables. However, using variables makes the solution easier for non-expert users of XSLT or XPath to maintain. These templates already implement a way to build the proper binding ID. You would not want to change how the ID gets generated when making a template to match a particular input element; instead, you would want to reuse the IDs, but change other information. For example, if you wanted to restrict an XML element named Level
to take only numeric input, you might write a bit of code as shown in Listing 2
(the code could be improved upon to achieve reuse).
The various formats use this same methodology to generate the bindings. Both the Forms namespace and XForms namespace generations call templates to output their content—for XForms the defaultbindelement
and for Forms the defaultformtext
. In the case of the actual document content, such as drawn controls and the text labels associated with them represented in templates in the oobuilder
mode, you can see that in the following code fragment:
The fragment outputs a paragraph with a reference to a template named commonvalue
which outputs an inline text element to hold the local-name()
of whatever element is being processed. It also outputs a drawn control and associates the correct identifier with it.