 | Level: Introductory Elliotte Rusty Harold (elharo@metalab.unc.edu), Adjunct Professor, Polytechnic University
07 Aug 2007 XForms uses XML Path Language (XPath) as its basic function and evaluation language.
This is the same XPath used in Extensible Stylesheet Language Transformations (XSLT). In addition to familiar functions like
count and substring, XForms
introduces a number of useful extension functions to XPath for numeric, date, and XForms-specific operations including if, avg,
min, max, now, days-from-date, month, and instance.
XForms adopts the familiar XPath expression language for two purposes. First, it uses it as a query language for addressing and identifying fields
within the form and the user-submitted data. In particular, it uses XPath expressions to bind input controls to particular parts of the form's data model. Second, it uses XPath as its basic calculation language for output,
bind, and setvalue elements. The focus of this article is mostly on
the second usage, though some of these functions are useful for binding as well.
XPath was designed to be extensible both by users and implementers.
XPath 1.0 defines a basic set of functions such as count and translate that are suitable for a broad range of purposes.
Languages such as XSLT, XQuery, XML Pointer Language (XPointer), XML Signature, and XForms that adopt the XPath expression language are expected to expand on this basic set with additional functions. XForms takes advantage of this by introducing new functions useful in forms processing. In particular, XForms adds functions for math, dates, times, Booleans, and XForms-specific operations.
You may never see these functions in an XSLT stylesheet or a Jaxen program, but you can use them today in any XForms 1.0 document.
XForms that calculate
I'll begin with a simple example. Listing 1 shows a basic XForm that displays today's date.
Listing 1. An XHTML document containing a simple XForm
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xforms="http://www.w3.org/2002/xforms">
<head>
<title>Date and Time</title>
<xforms:model>
<xforms:instance xmlns="">
<Name/>
</xforms:instance>
</xforms:model>
</head>
<body>
<h1>
<xforms:output value="now()">
<xforms:label>The time is now: </xforms:label>
</xforms:output>
</h1>
</body>
</html> |
Figure 1 shows this form running in Firefox with the Mozilla XForms plug-in.
Figure 1. The date is supplied by the now function
The xforms:output element evaluates the XPath 1.0 expression contained in its value attribute. It inserts this result into the browser's view of the document. In this example, the XPath expression consists solely of
the function now(), which returns the current date and time, formatted according to ISO 8601.
That is, year-month-day:hours:minutes:seconds-timezone.
This is the time format recommend by the XML Schema specification.
Listing 1 is straightforward and obvious except for one thing:
Where the heck did the now() function come from?
XSLT 1.0 and XPath 1.0 do not have a now() function. Indeed, including one would contradict XSLT's basic nature as a functional programming language. A stylesheet that uses a now() function produces different results depending on when it is run. However, XForms does not have the same goals and constraints as XSLT. XForms is neither functional nor Turing complete.
Rather, it is declarative. Therefore,
functions such as now() that don't fit into XSLT fit nicely with XForms. With that in mind, I'll explore the other new functions XForms 1.0 introduces.
Dates and times
2007-06-10T08:54:21-05:00 is complete, but it's not exactly pretty.
XForms adds four more functions for converting dates into individual pieces such as years and months:
-
days-from-date()
-
seconds-from-dateTime()
-
seconds()
-
months()
The first two operate on date-times and can be used
on the output of now()
as well as on date values input by the user or supplied by the form itself.
The second two operate on durations, also in the format specified by ISO 8601 and the World Wide Web Consortium (W3C) XML Schema Language.
days-from-date()
The days-from-date function converts a date in the form 2007-07-30T08:54:21-05:00 or 2007-07-30 into the number of days that have elapsed since January 1, 1970. You can use this to calculate the difference between two dates or the amount of time that has passed. For example, Listing 2 counts the days remaining until Christmas:
Listing 2. An XForm that counts the days remaining until Christmas
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xforms="http://www.w3.org/2002/xforms">
<head>
<title>Days until Christmas</title>
<xforms:model>
<xforms:instance xmlns="">
<variables>
<Now/>
<Year/>
<Christmas/>
<ChristmasDay/>
<Result/>
</variables>
</xforms:instance>
<xforms:bind nodeset="Now" calculate="days-from-date(now())"/>
<xforms:bind nodeset="Year" calculate="substring(now(), 1, 4)"/>
<xforms:bind nodeset="Christmas" calculate="concat(../Year, '-12-25')"/>
<xforms:bind nodeset="ChristmasDay" calculate="days-from-date(../Christmas)"/>
<xforms:bind nodeset="Result" calculate="../ChristmasDay - ../Now"/>
</xforms:model>
</head>
<body>
<h1>
<xforms:output value="Result">
<xforms:label>Days until Christmas: </xforms:label>
</xforms:output>
</h1>
</body>
</html> |
 | |
Compressing the form
This could be written with one long complicated XPath expression like this one:
days-from-date(concat(substring(now(), 1, 4), '-12-25'))
- days-from-date(now()) |
However, it seems much clearer with the temporary variables.
|
|
In this case, the instance holds five temporary variables:
Christmas, ChristmasDay, Now, Year, and Result. These five
variables are set by XPath expressions in bind elements.
-
Now is set by using days-from-date to get
the number of days that have elapsed since January 1,
1970 until today.
-
Year is set by picking off the first four digits of
today's date with a substring operation.
-
Christmas is set by appending "-12-25" to the Year
retrieved in the previous step.
-
ChristmasDay is set by using days-from-date on
Christmas.
- The form subtracts
Now from ChristmasDay to get the
number of days remaining.
The final result is displayed by an output element.
This form does not yet handle
dates after December 25. I'll introduce one more
extension function to help with that shortly.
seconds-from-dateTime()
The seconds-from-dateTime function
converts a date in the form 2007-07-30T08:54:21-05:00 into the
number of seconds that have elapsed since midnight,
January 1, 1970, Greenwich Mean Time. You can use this to
calculate the difference between two times or the amount of
time that has passed. For example, this output element counts the seconds remaining
until Christmas:
<xforms:output value="seconds-from-dateTime(
concat(substring(now(), 1, 4), '-12-25T00:00:00-05:00'))
- seconds-from-dateTime(now())">
<xforms:label>Seconds until Christmas: </xforms:label>
</xforms:output> |
seconds-from-dateTime returns NaN if it can't convert the string to a date.
seconds()
The seconds() function operates on
durations rather than datetimes. It takes a string in the form
P3DT10H30M1.5S (3 days, 10 hours, 0 minutes, and 1.5 seconds)
and converts it into just seconds (though possibly fractional).
This is useful for comparing durations and adding and
subtracting them.
months()
The months() function also operates on
durations. It takes a string in the form P2Y7M3DT10H30M1.5S (2
years, 7 months, 3 days, 10 hours, 0 minutes, and 1.5 seconds)
and converts it into just months. Days, hours, minutes, and
seconds are truncated.
There are no corresponding days(), hours(), or minutes()
functions. If you want those, you have to ask for seconds;
then do the math.
Numeric functions
XForms adds four numeric functions to XPath 1.0:
-
avg()
-
min()
-
max()
-
count-non-empty()
avg()
The avg() function calculates the arithmetic mean
of a node-set. That is, it converts each of the nodes in the set to a number, adds them up, and divides the result by the number of nodes.
If any of the nodes in its argument node-set
can't be converted to a number, avg() returns NaN.
min() and max()
The min() and max() functions also take node-sets as
arguments. Each member is converted to a number. The min() function returns the minimum value, and
the max() function returns the maximum
value. If any of the arguments can't be converted to a number,
these functions return NaN.
For example, the XForms model in Listing 3
provides several temperature measurements. (The user would use a repeating control to fill them in.) Then
bind elements calculate the minimum, maximum, and average of all the temperature measurements and assign them to other elements.
Listing 3. An XForms model that calculates high, low, and average temperatures from multiple measurements
<xforms:model>
<xforms:instance xmlns="">
<Data>
<Temperature/>
<LowTemperature/>
<HighTemperature/>
<AverageTemperature/>
</Data>
</xforms:instance>
<xforms:bind nodeset="AverageTemperature" type="xs:decimal"
calculate="avg(../Temperature)"/>
<xforms:bind nodeset="LowTemperature" type="xs:decimal"
calculate="min(../Temperature)"/>
<xforms:bind nodeset="HighTemperature" type="xs:decimal"
calculate="max(../Temperature)"/>
</xforms:model> |
count-non-empty()
The XPath built-in count() function
returns the number of nodes in a node-set. The XForms extension
function count-non-empty() returns the
number of non-empty nodes in a set, where "empty" is defined as
having a string representation that is not empty.
For example, to calculate the average
price of several items, first sum up all the individual prices
with an expression like sum(../Price). Then divide the sum
by count-non-empty(../Price).
<xforms:output value="sum(../Price) div count-non-empty(../Price)">
<xforms:label>Average price: </xforms:label>
</xforms:output> |
By contrast, if you divide by count(../Price), the average price might be
too low if there are extra fields in the form that the user
simply hasn't filled in.
Boolean functions
XForms adds two Boolean functions to XPath 1.0:
-
if()
-
boolean-from-string()
if()
XSLT doesn't need if() because
it has xsl:if. However, XForms doesn't have a selection operator,
so it needs to add the if() function to XPath.
The if() function takes three arguments:
- The test condition
- The true result
- The false result
That is,
if (
condition
,
trueresult
,
falseresult
)
|
The if() function is like the ternary ?: operator in the Java™ and C programming languages.
For example, Listing 4 shows a revised version of the days
until Christmas model that takes into account the
possibility that the form might run on Boxing Day (the day after Christmas).
To do this, it calculates both possible results, then uses if() to choose between them depending on
whether the first result is negative.
Listing 4. An XForm that counts the days remaining till this Christmas or next Christmas
<xforms:model>
<xforms:instance xmlns="">
<variables>
<Now/>
<Year/>
<Christmas/>
<ChristmasDay/>
<NextChristmas/>
<NextChristmasDay/>
<Result/>
<Result1/>
<Result2/>
</variables>
</xforms:instance>
<xforms:bind nodeset="Now" calculate="days-from-date(now())"/>
<xforms:bind nodeset="Year" calculate="substring(now(), 1, 4)"/>
<xforms:bind nodeset="Christmas" calculate="concat(../Year, '-12-25')"/>
<xforms:bind nodeset="NextChristmas" calculate="concat(../Year + 1, '-12-25')"/>
<xforms:bind nodeset="ChristmasDay" calculate="days-from-date(../Christmas)"/>
<xforms:bind nodeset="NextChristmasDay"
calculate="days-from-date(../NextChristmas)"/>
<xforms:bind nodeset="Result1" calculate="../ChristmasDay - ../Now"/>
<xforms:bind nodeset="Result2" calculate="../NextChristmasDay - ../Now"/>
<xforms:bind nodeset="Result"
calculate="if(../ChristmasDay - ../Now >= 0,
../Result1,
../Result2) "/>
</xforms:model>
|
boolean-from-string()
The boolean-from-string() function is semi-redundant with XPath's built-in boolean() function, but only half.
The boolean-from-string() function follows the W3C schema definition of Boolean. The boolean() function follows the XPath definition of Boolean.
In W3C schemas, the strings "true" and "1" are true. The strings "false" and "0" are false. All other strings are false.
By contrast, in XPath, all non-empty strings are true, including "false" and "0".
Furthermore, boolean() has specific rules for determining the truth value of
numbers and node-sets.
The boolean-from-string() function simply converts arguments of other types to strings and then tests their truth.
XForms functions
XForms defines three functions purely for its own use that make no sense elsewhere:
-
instance()
-
property()
-
index()
instance()
When an XForms model contains multiple instances, the instance() function selects the one to work with.
For example, suppose the days-until-Christmas calculation is
just a piece of the page's user interface, but you don't really
want to send it back to the server. There's other data you'd
send back to the server, as shown in Listing 5. Here the real
data is in the instance with the ID orderInfo and the temporary
variables are in the instance with the ID temporaryVariables.
Listing 5. An XForm that contains two instances
<xforms:model>
<xforms:instance xmlns="" id="temporaryVariables">
<variables>
<Now/>
<Year/>
<Christmas/>
<ChristmasDay/>
<NextChristmas/>
<NextChristmasDay/>
<Result/>
<Result1/>
<Result2/>
</variables>
</xforms:instance>
<xforms:instance xmlns="" id="orderInfo">
<Order>
<Item/>
<Price/>
</Order>
</xforms:instance>
<xforms:bind nodeset="Now" calculate="days-from-date(now())"/>
<xforms:bind nodeset="Year" calculate="substring(now(), 1, 4)"/>
<xforms:bind nodeset="Christmas"
calculate="concat(instance('temporaryVariables')/Year, '-12-25')"/>
<xforms:bind nodeset="NextChristmas"
calculate="concat(instance('temporaryVariables')/Year + 1, '-12-25')"/>
<xforms:bind nodeset="ChristmasDay" calculate="days-from-date(../Christmas)"/>
<xforms:bind nodeset="NextChristmasDay"
calculate="days-from-date(instance('temporaryVariables')/NextChristmas)"/>
<xforms:bind nodeset="Result1"
calculate="instance('temporaryVariables')/ChristmasDay - ../Now"/>
<xforms:bind nodeset="Result2"
calculate="instance('temporaryVariables')/NextChristmasDay
- instance('temporaryVariables')/Now"/>
<xforms:bind nodeset="Result"
calculate="if(instance('temporaryVariables')/ChristmasDay
- instance('temporaryVariables')/Now >= 0,
instance('temporaryVariables')/Result1,
instance('temporaryVariables')/Result2) "/>
</xforms:model> |
Rather than assuming a particular root node, the bind elements now
specify the one they want using instance('temporaryVariables').
Other bind elements can bind to the second instance with
instance('orderInfo').
Indeed, you can even combine data from two or more instances in one XPath expression.
property()
The property()
function returns the value of an XForms property. XForms only defines
two properties:
- version
- The version of XForms supported: 1.0 for XForms 1.0, 1.1 for XForms 1.1, and so forth.
- conformance-level
- The profile of XForms in use, typically
"full" for a complete implementation.
Specific XForms processors may define additional properties beyond these two standard ones. Usually these are namespace qualified.
index()
The index() function returns
the current position of a repeat element.
That is, you pass the index of the repeat as the
argument, and the function returns the current item in that repeat.
It's useful for operating on the item the user is currently working with.
Extension functions
The fun doesn't stop there. Individual vendors are allowed (and even encouraged) to expand on the basic function library with additional functions of their own. For example, MozzIE 1.8 adds the EXSLT math and date-and-time
extension functions. The math functions
include abs(),
acos(),
asin(),
atan(),
atan2(),
constant(),
cos(),
exp(),
highest(),
log(),
lowest(),
sin(),
sqrt(), and
tan().
The date-and-time functions include add(),
add-duration(),
date(),
date-time(),
day-abbreviation(),
day-in-month(),
day-in-week(),
day-in-year(),
day-name(),
day-of-week-in-month(),
difference(),
duration(),
format-date(),
hour-in-day(),
leap-year(),
minute-in-hour(),
month-abbreviation(),
month-in-year(),
month-name(),
parse-date(),
second-in-minute(),
sum(),
time(),
week-in-month(),
week-in-year(),
year(), and
date-for().
However, you should be aware that an XForm that uses any of these will only work in MozzIE. It will not work in formsPlayer, Orbeon Forms, Chiba, or Firefox (at least not until they also implement these EXSLT functions). Therefore, you probably want to stick to the standard XForms 1.0 set for broad deployment. Still, these might be useful in an Intranet environment where you know everyone has the same form processor.
An XForm that contains non-standard XPath extension functions should declare them in
a functions attribute on the model element. Listing 6 shows how to declare some EXSLT extension functions
for trigonometry and dates.
Listing 6.
An XForm that declares extension functions
<xforms:model
xmlns:date="http://exslt.org/dates-and-times"
xmlns:math="http://www.exslt.org/math"
functions="math:cos math:sin date:add date:year">
<xforms:instance xmlns="" id="temporaryVariables">
...
</xforms:instance>
...
</xforms:model> |
When the functions attribute is present on the model,
the XForms processor reads it before loading the form.
If it sees an extension function it doesn't recognize,
it halts processing by signaling an
xforms-compute-exception event.
In conclusion
The X in XPath stands for extensible. XForms has extended XPath
to provide extra power and unique features that are worth
learning. While XForms supports the full range of XPath 1.0
you're used to from XSLT, it has quite a bit more. Some of
these functions like days-from-date()
and now() are just nice to have. Others
like if() and instance() are critical components of any
non-trivial XForm. More XForms use these functions than
don't. Learn to take advantage of the XForms XPath extension
functions and you too can be an XForms master.
Resources Learn
- "Why XForms?" (developerWorks, October 2006) explains the goals that might lead one to an XForms solution.
- "XForms in Firefox" (developerWorks, January 2007) introduces XForms in a browser environment.
- Wikibooks is working on a basic XForms Tutorial and Cookbook.
-
XML in a Nutshell
(Elliotte Rusty Harold and W. Scott Means, O'Reilly, 2005) includes a complete reference and brief tutorial on XPath.
- "Get started with XPath" (developerWorks, May 2004) is a tutorial for people who do not know XPath or who want a refresher.
- Read "Introduction to XForms, Part 1: The new Web standard for forms" (developerWorks, September 2006) to learn about XForms and its capabilities.
-
EXSLT is a semi-standard library of XPath extension functions for XSLT, but XForms processors can implement them too.
- Tyler Anderson explains how to "Dynamically create controls with the repeat, select1, and itemset elements" (developerWorks, January 2007).
- The XForms Working Group Charter presents the original, official goals of XForms.
- Read Wikipedia's XForms: a customarily excellent entry.
- The book
XForms Essentials
(Micah Dubinko, O'Reilly, 2003) explains how to create XForms.
-
IBM XML certification: Find out how you can become an IBM Certified Developer in XML and related technologies.
-
XML: See developerWorks XML Zone for a wide range of technical articles, tips, tutorials, standards, and IBM Redbooks.
-
developerWorks technical events and webcasts keep you current.
Get products and technologies
-
IBM trial software: Build your next development project with trial software available for download directly from developerWorks.
Discuss
About the author  | 
|  | Elliotte Rusty Harold is originally from New Orleans, to which he
returns periodically in search of a decent bowl of gumbo. However, he
resides in the Prospect Heights neighborhood of Brooklyn with his wife
Beth and cats Charm (named after the quark) and Marjorie (named after
his mother-in-law). He's an adjunct professor of computer science at
Polytechnic University, where he teaches Java and object-oriented
programming. His Cafe au Lait
Web site has become one of the most popular independent Java sites on
the Internet, and his spin-off site, Cafe con Leche, has become one
of the most popular XML sites. His next book, Refactoring HTML, will be published by
Addison Wesley later this year.
He's currently working on the XOM API for processing XML and the Jaxen XPath engine. |
Rate this page
|  |