The past few weeks I have been working enabling data formatting of input fields in Orbeon. Orbeon has a custom element that can be used to unformat and format input fields. It only made sense to use these elements such that formatting is handled by Orbeon itself.
The values for these elements must be XPath expressions which will be evaluated at run-time by Orbeon. The complexity comes from the fact that a huge variety of data formats has to be supported, as the formats customers can use is kept very open.
This means we will need to generate a XPath expression that will interpret the format-string and translate the input at runtime, which is done with XSLT. The expression itself has to be known when the form is deployed and thus is static. This has to be done for a couple of supported data types, which can be divided into three groups: “String”, “Decimal” and “Date/Time”.
The “String” type is straight-forward as we only allow uppercase and lowercase formatting, which is handled by the XPath function ‘lower-case’ and ‘upper-case’. Which case we have can easily be determined by the format-string which is of the form: “3|3”
The “Decimal” Type is already a bit more complex because we have multiple group and decimal separators and different negative and positive prefixes. This all leads to values such as ($ 18.333,17) to represent the number -18333.17. The format-string is of the form: “2|-||||,|.|3|1|2|2|$|-”
Each element is separated by the pipe (‘|’) symbol and has its meaning. This way we know what to look for. It is now not possible anymore to simply use the XPath function ‘format-number’ as the input is not an xs:decimal. To fix this we first need to unformat the input to a value that is of the type xs:decimal.
The most complex type is “Date/Time”, the format-string is of the form: “1|EEE, d MMMMM, yyyy” or “1|dd/MM/yyyy HH:mm”. This is hard to parse as there is no clear separator. With the “Decimal” type at least you knew what to look for, with the “Date/Time”, it is required to first parse the format-string to know the location of each element, and determine the separator between the element and look for that same pattern in the input.
This requires a lot of recursive calls in XSLT to progressively iterate over the string. The “Date/Time” will be unformatted to the ISO format, which can be formatted using the XPath function ‘format-dateTime’. A problem with this function is that it does not accept our type of format-string, which means we have to convert the format-string itself as well. This too requires a lot of recursive XSLT function calls.
Once that worked, two other problems had to be taken care of.
- Orbeon has a set of magic dates that it tries to interpret before the unformat/format elements are evaluated. In some cases this would prevent the unformat and format from recognizing the data.
To fix both problems some minor changes had to be done to the Orbeon engine. When creating the input fields, a check is performed to see if there is a format element present. If so, then the format-string of the ‘format-date’ element is taken and placed on the input field such that it is available in the front-end for the datepicker. This will also be used to determine if the magic date conversion have to happen. The datepicker has to implement it’s own formatting to convert the date from the format to the ISO format it understand.
Looking back at the project, I realize that there is too much code written in XSLT which becomes complicated because of some limitations. It would have been far better to write an extension function and just write the entire unformat/format expression generation in Java. I guess that’s something that will be done in the future, when some maintenance work has to happen.