previous article on XForms in OpenOffice showed how to create a simple XForm in OpenOffice. My introduction to this particular technology came while working on the Danish UBL project, which needed a cross-platform implementation for some of the 23 UBL 2.0 document types intended for use by small businesses.
Unfortunately, it proved impossible to create this solution purely in XForms in OpenOffice. The project required supporting documents containing all possible elements, but the UBL Order implementation ran into performance problems, because, as described in the previous article, it keeps every binding in memory; therefore, a large number of bindings slows down the implementation measurably. Obviously, the complexity of expressions can also affect this. In my experience, forms that exceed several hundred bindings are unusable.
A 10-line UBL Order was likely to have 500 or more bindings, and thus was not usable in an OpenOffice XForm. Nonetheless, the route to discovering this depressing information did produce some useful tools and techniques for others who want to work with smaller XML documents in OpenOffice.
Importing XML Formats into OpenOffice
Because OpenOffice’s internal representation is ODF XML, it is natural to generate ODF from other XML formats using an XSLT transformation. This applies equally well to an XForm inside of an ODF document.
|Author’s Note: To work with the code in this article, you need OpenOffice version 2.4 (earlier versions not tested) with the Java runtime enabled. The OpenOffice XSLT implementation currently uses Xalan as its XSLT processor. However, as this article was being prepared for publication it was announced that OpenOffice 3.1 will use Saxon, which will allow future filters to be written using XSLT 2.0. The sample filter uses XSLT 1.0, because that’s the XSLT version supported in the current OpenOffice release.|
You always need to take a few things into account when making an XSLT transform, such as the processor and the context (web server, command line application, etc.). OpenOffice adds yet another consideration: You have the possibility of running the transformation as an XSLT Filter. An XSLT Filter combines an XSLT stylesheet and some OpenOffice settings registered in OpenOffice that allows OpenOffice to open various XML-based formats and work with them in the context of the application.
This article explains how to create an OpenOffice XSLT Filter that applies generically to all example XML instances—in other words, it’s as a basic tool for working with XML in OpenOffice XForms.
|Figure 1. Generic XSLT Model: The generic XSLT filter generates ODF instances from any number of input formats.|
What the Filter Should Do
A generic filter should be able to accept any input XML, and generate an XForm and the OpenOffice form fields and bindings to allow users to edit that XML, excluding mixed content formats which are not suited to form based editing. Because it is generic, you can reuse it within other filters for specific formats (see Figure 1).
While the model looks simple, the implementation is far more difficult. Remember that the content in an OpenOffice XForm is separated in several sections. The Form fields, bindings, and XML instance are in different parts of the document, but they all need to be able to point back at each other, as shown in Figure 2.
|Figure 2. OpenOffice XForms Relationships: The diagram shows how the different parts of the OpenOffice XForms implementation relate to each other.|
To build these relationships, the transform uses three different modes, because it must process the same input tree three different times.
There are other filter processing limitations as well. For example, OpenOffice restricts you to using Xerces, its built in processor, which limits you to XSLT 1.0. However, you could run the XSLT stylesheet outside of OpenOffice, using a different parser, and then use another process to assemble the zipped OpenOffice document from the generated ODF with XForms document.
|Figure 3. Format-Specific Transform: Input format #4 uses a format-specific transformation, but includes the Generic XSLT transform as a library to enable the transform.|
Generic solutions often suffer from reusability problems; the twists and turns that make the solution generic may also make them unsuitable for specific solutions. That’s not true here. This transform should work in conjunction with any specific format transformation to generate a generic ODF document with an embedded XForm as shown in Figure 3.
Generic Transform Limitations
Because the generic solution must accept any input for which an XForms model can be generated, it generates primarily text field form elements. A transformation written to use XML Schema to generate XForm bindings would be able to use the schema type information to determine that an input field should be a radio button or some other control, but when generating an XForm from a sample input without a schema transformations are limited to generating text fields.
To make sure that form fields get output in appropriate locations, the transform places controls relative to the paragraphs that contain them, using the text:anchor-type attribute. Among other benefits, this frees the process from having to calculate X and Y coordinates for the controls.
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.
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 * and @* 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.
Listing 1 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.
XForms submission relies on three templates. The first is the submitForm template:
The submitForm template generates the Xforms submission binding, and in this case associates it with an action to output to the file protocol, via the action attribute. That requires two global parameters: outputpath and outputfile. If, instead of using an XSLT Filter, you transformed your input XML via your own code for calling transformations, you could set the value of these global parameters in your code and then place the output ODF into the zipped file format that OpenOffice uses.
The drawn control for the submission button to be bound to is in the template matching the root node (the first template in the stylesheet). The relevant fragment below shows the drawn element with button positioning:
Finally, here’s the template that controls the form button.
Note that in most XForms customization cases in OpenOffice, you can add customization in three places:
- At the XForms namespaced elements
- At the forms namespaced elements
- At the drawn elements inputs
This rule also holds true for customizing the submission button.
A number of customization possibilities exist for extending the generic transformation, including:
- Customization at the level of matching specific input elements or attributes
- Customizing the default behavior
- Customization of the generated OpenOffice styles
- Customizing the default objects generated in the output
Some obvious customizations are to make the XForms Instance output filename dependent on the value of a form field, or to make it submit to an HTTP address using post, or perhaps to require form fields to be generated only for XML elements or attributes that do not already have a value.
Importing the Filter and Generating a Package
Making a new filter in OpenOffice using XSLT can be somewhat tedious. The process consists of associating the filter with a file extension and a filter name, choosing either an import or export XSLT or both, and then generating a package for your filter.
Step 1—Turn the XSLT into a Filter.
Launch OpenOffice and choose the menu Tools (XML Filter Settings as shown in Figure 4). You’ll see the XML Filter Settings dialog shown in Figure 5, which lists your saved filter settings.
|Figure 6. Defining a New Filter: Fill in the appropriate values in both the General and Transformation tabs.|
You can create a new filter, edit an existing filter, delete filters, or save filters as a package. You can delete a filter by clicking the Delete button. Close and then reopen the Filter dialog to ensure the filter was deleted. If your new filters don’t appear here something is wrong.
In this case, click the New button to create a new filter.
You enter the data for your new filter in a tabbed popup window with two panels, the first panel is named General and controls such things as the Filter name, the second is Transformation where you will put in values for the XSLT. This form is shown in Figure 6.
Change the Filter Name to something descriptive, such as “Generic XForms.” Change the Application field to odt, and change the file type to XFORMS-INPUT, but leave the file extension set to xml. The General tab should now look like Figure 7.
|Figure 7. Filter Settings Example: Here’s how the first panel of your new filter’s settings should look if you followed the suggestions in this article.|
Next, switch to the Transformation panel. This panel has only one input field of interest, a field which has the label ‘XSLT for Import.” Browse to the GenericFilter.xsl file and select it. When the path shows up in the input field, click OK. The dialog will close. You should now see the new filter in the XML Filter window.
At this point you should be able to open any XML document that is not a mixed content document and edit it as an XForm.
The following examples show the forms generated for three separate XML examples, along with the XML input data that generated each form.
Example 1: Params File (see Figure 8)
|Figure 8. Params File: The params file is an extremely flat file structure in which there are only two types of inputs: the param element and its name attribute.|
The XML input for the preceding XForm shows how attributes in the default implementation are implemented following the element inputs. A typical configuration would be to generate a table and to put the element in one table column, and the attributes in another one.
2007 01 20 structuredDK DKK 5 true default Invoice22.xml D:/projects/tests/work/xforms/OO/ exampleimplementation/output/
Example 2: Address Data (see Figure 9)
|Figure 9. Address Form: This form replicates the functionality of the form shown in the earlier tutorial “Getting Started with XForms in OpenOffice.”|
This example uses the element names to give names to input fields (this is of course configurable).
Example 3: UBL Reminder Form (see Figure 10)
|Figure 10. Reminder Document: This example XForm was generated from a larger document—a UBL Reminder document. The full form is not shown as it covers several pages.|
Listing 3 shows the full form code.
To sum up, this article demonstrated a method to generically transform XML input formats to generate XForms that will run in OpenOffice so users can edit the XML input values. The XSLT used in the article is sufficiently configurable that you can maintain, extend, and reuse it relatively easily without breaking functionality.