A previous article, "What is the IBM data access tool?," introduced the data access tool in the IBM Workplace Client Technology, rich edition. In a subsequent article, "Creating a database application with the IBM data access tool," we showed how to create a call tracking application using a sample database application. In this article, we describe how to use XML Path Language (XPath) with the IBM data access tool using another sample application, the Birthday Journal application. XPath is a language for addressing parts of an XML document (see the WC3's XML Path Language recommendation for more information). Within the data access tool, XPath is used as a declarative scripting language. As the application designer, you can use XPath expressions to perform simple computations within a database application.
This article explains the development of a database application that makes extensive use of XPath. It describes where XPath can be used in the design of an application and provides some sample expressions that you can reuse. This article assumes that you are already familiar with the IBM data access tool and how to create a database application. For more information about the data access tools, see the IBM Workplace Resource Center.
You can use XPath expressions for the following:
- Default values
- Computed fields
- Simple actions
When creating a database table, you can specify a default value for a column using an XPath expression. Beware when specifying default values for text columns. If the intent is to specify a string literal, such as a fixed string value such as default, then the correct way to express this in XPath is to enclose the string in single quotes. You must type 'default', including the single quotes, to specify the literal string value "default." If you type the word default without any quotes, it most likely will evaluate to an empty string when this is evaluated as an XPath expression.
The computed expression for a computed field is an XPath expression. The computed field can show values calculated using the data in the current record or the entire current table. The IBM data access tool supports all the XPath 1.0 and XForms 1.0 XPath functions as well as some data access specific functions, which are listed and described at the end of this article.
The following simple actions use XPath expressions to calculate the action parameters:
- Goto Record: The index of the record to be selected
- Set Value: The value to be set
The IBM data access tool comes with some basic XPath editing capabilities. The Attributes editor in the data access designer lists the fields and functions that can be used in an XPath expression. You can reference the values in the currently selected record or any record in a table. Consider the following table definition:
| Column name | Data type | Length | Nullable | Default value | UI control type |
| UNID | TEXT | 64 | No | none | Edit |
| FirstName | TEXT | 30 | No | none | Edit |
| LastName | TEXT | 30 | No | none | Edit |
| DateOfBirth | DATE | n/a | n/a | none | Edit |
| Month | INTEGER | n/a | n/a | none | Combination Box |
| Day | INTEGER | n/a | n/a | none | Edit |
The XML instance data associated with this table looks something like this:
<DataGraphRoot> <Birthdays UNID="1" FirstName="Andrew" LastName="Grissend" DateOfBirth="1945-01-01" Month="1" Day="1" /> </DataGraphRoot> |
To reference the value in the FirstName column for the current record, use the XPath expression @FirstName. The Attributes editor in the data access designer lists all of the fields that are available in the current table. You can insert the expression for a field by selecting the field name from the list of fields and clicking the Insert button.
Figure 1. Fields list on the Computed Expression tab in the Attributes editor
The data access designer also lists all supported functions. You can insert a function by selecting the function name from the list of functions and clicking the Insert button.
Figure 2. Functions list on the Computed Expression tab in the Attributes editor
Some validation of the XPath syntax is also provided; if the expression is invalid an error message is displayed under the editing area.
Figure 3. Error message on the Computed Expression tab in the Attributes editor
The remainder of this article demonstrates some examples where you can use XPath. You can do the following with XPath expressions:
- Automatically generate unique IDs for the primary key column of each record.
- Parse date values to extract the month and day.
- Dynamically create text messages using data from the current record.
- Compute values to be saved in the current record.
The sample application that comes with this article is a simple Birthday Journal that allows you to record people's birthdays. Here is how to run the sample application:
- Download the sample application JAR file.
- Import the sample application into the data access designer.
- Open the application.
Refer to the Application catalog help for more information on importing and opening applications.
The Birthday Journal form opens by default.
Figure 4. Birthday Journal default design element
This form displays statistics about the data stored in the database table for this application. All of the information is calculated dynamically using XPath expressions. The application also provides a form to allow new birthday entries to be created in the database.
Figure 5. Birthday Entry form in the data access viewer
This form allows you to enter the person’s first name, last name, and date of birth. As we will see later, XPath is used to dynamically display the person’s age and to dynamically calculate the month and day of the person’s birthday. The day and month of a person's birthday are saved separately to simplify some of the computations performed later in the article.
This form also has a search option that allows you to search for birthday entries by first name, last name, date of birth, day of birth, and month of birth.
The application also includes one grid that lists all birthday entries by name and birthday.
Figure 6. All Birthdays grid in the data access viewer
We perform the following steps to create the Birthday Journal sample application:
- Create the data definition.
- Create the Birthday Entry form.
- Create the Birthday Journal form.
- Create the All Birthdays grid.
The data definition for this application consists of a single table. The columns and values of this table are the same as those in the table shown in the section titled "Simple actions" earlier in this article.
For all data access applications, each record that is created must contain a unique primary key value. This can be tedious for the user to enter, so in this sample, we use a default value expression to dynamically determine a unique value.
The first XPath expression we use generates a unique value for the UNID column in each new record. This expression generates a numeric value that starts at 1 and increments each time a new record is created. The expression looks like this:
if(ibmforms:record-count()=0, '1', string(max(ibmforms:current-table()/@UNID)+1))
The expression uses the if() function, which is defined in the XForms 1.0 specification. This function evaluates the first parameter as a Boolean value, and if it is true, returns the second parameter; otherwise, it returns the third parameter.
The first parameter in the expression is ibmforms:record-count()=0. The ibmforms:record-count() function is a custom data access function that returns the record count for the current table. The complete expression tests whether or not the number of records is equal to zero.
The second parameter in the expression is '1', which is a string literal. If the number of records is zero, then the overall expression returns ‘1’.
The third parameter string(max(ibmforms:current-table()/@UNID)+1) is an expression that determines the maximum UNID value, max(ibmforms:current-table()/@UNID), then adds one to it and converts the result to a string using the string() function. If the number of records is not zero, then the overall expression returns a new UNID value that is one larger than the current maximum value.
NOTE: This solution works because we create a single user application. If multiple users access the database application simultaneously, it is possible that conflicting UNID values would be generated.
The sample also sets a default value for the FirstName, LastName, and DateOfBirth columns which are just simple empty string literals, such as '', and the now() function, which returns the current system date and time as a string value.
Create the Birthday Entry form
The Birthday Entry form is used to save the date of birth for one person by creating a record in the Birthdays table. The form contains fields to allow the user to enter the person's first name, last name, date of birth, day of the month, and month of the person's birthday. There are also some controls on the form to allow new entries to be created, deleted, and saved and also to navigate through the existing entries. For the purposes of this article, we focus on areas where XPath is used. Refer to the data access designer help for more information on working with controls and events. In the data access designer, the form looks like Figure 7.
Figure 7. Birthday Entry form in the data access designer
One thing that is immediately obvious is that the controls for entering the day of the month and month of the person's birthday are not shown by default in the data access viewer (see Figure 2). The reason for this is that in normal use when a new entry is being created, we can automatically determine these two values based on the person's date of birth. The controls are added to the form so that when a search is performed, the user can search using these values; and they are hidden using the form On Load event. Also, there is no field to enter the UNID value because a default value formula specified earlier generates a unique default value. There is no need to require the user to enter this value. The form contains a number of computed fields that use an XPath expression to display the following information:
- The person's age
- The current record index and count
- The value of the UNID field
- The year the person was born
- The month the person was born
- The day of the month the person was born
To automatically set the Month and Day fields in each record, we use the On Value Changed event of the DateOfBirth field (control edit_4). The On Value Changed event is triggered every time the user enters a new date for the DateOfBirth field and also when the user navigates between records within a form. The Set Value simple action is executed twice to set the Month and Day fields when the DateOfBirth changes. The Value argument of the Set Value simple action is an XPath expression. We need two expressions that calculate the Month and Day fields based on the DateOfBirth field. Select the Birthday control, and then switch to the Event tab to see the simple actions. The DateOfBirth field is formatted using the format YYYY-MM-DD where:
- YYYY is the year.
- MM is the month.
- DD is the day of the month.
To extract the Month field, we use the following XPath expression:
substring(@DateOfBirth,6,2)
and to extract the Day field, we use:
substring(@DateOfBirth,9,2)
The substring() function returns the substring of the first argument starting at the position specified in the second argument with length specified in the third argument.
To display the person's age, we use the following XPath expression:
concat(@FirstName, ' is ', substring(now(),1,4) - substring(@DateOfBirth,1,4)
- number(if(@Month <= substring(now(),6,2) and @Day <= substring(now(),9,2), '0', '1')),
' years old')
The concat() function returns the concatenation of its arguments. In our case, this is @FirstName, the value in the FirstName field; ' is ' is a string literal.
substring(now(),1,4)[1] - substring(@DateOfBirth,1,4) [2] - number(if(@Month <=
substring(now(),6,2) and @Day <= substring(now(),9,2), '0', '1')) [3]
This expression computes the person's age by subtracting the year of birth [2] from the current year [1] and subtracting 1 if his or her birthday is equal to or earlier than today's date [3]. The string ' years old' is a string literal. To display the current record index and count, we use the following XPath expression:
concat('Birthday ', string(ibmforms:record-index()), ' of ', string(ibmforms:record-count()))
The ibmforms:record-index() function returns the index of the currently selected record. The ibmforms:record-count() function returns the index of the record count. To display the value of the UNID field, we use the following XPath expression:
concat('UNID: ', @UNID)
To display the year the person was born, we use the following XPath expression:
concat('Year: ', substring(@DateOfBirth,1,4))
To display the month the person was born, we use the following XPath expression:
concat('Month: ', substring(@DateOfBirth,6,2))
To display the day of the month the person was born, we use the following XPath expression:
concat('Day: ', substring(@DateOfBirth,9,2))
Create the Birthday Journal form
The Birthday Journal form is used as the default design element for the application. It displays summary information about the birthday entries and also can be used to go directly to the other design elements by clicking the respective buttons. The summary information is calculated using XPath expressions. In the data access designer, the form looks like Figure 8.
Figure 8. Birthday Journal form in the data access designer
The Birthday Journal displays the following summary information:
- Number of birthday entries
- Number of birthdays this month
- Number of birthdays remaining this month
- Next birthday this month
- Number of birthdays already this year
To display the number of birthday entries, we use the following XPath expression:
string(ibmforms:record-count())
To display the number of birthdays this month, we use the following XPath expression:
count(ibmforms:current-table()[@Month=number(substring(now(),6,2))])
In this expression, we use a dynamic predicate; that is, we start with all the records in the current table [ibmforms:current-table()], and then filter a subset whose Month field is set to the current month using [@Month=number(substring(now(),6,2))], and then return the number of elements in this node set using the count() function.
To display the number of birthdays remaining this month, we use the following XPath expression:
count(//Birthdays[@Month=number(substring(now(),6,2))][@Day>=
number(substring(now(),9,2))])
In this expression, we use a different syntax to get all the records in the current table (//Birthdays). This syntax is an alternative to using ibmforms:current-table(). If the XPath expression is used within a simple action, the current-table() function is required to get the expected behavior. In this expression, two dynamic predicates are used. The first predicate is the same as the previous example and returns a node set containing all birthday entries this month. This node set is used as the input for the second predicate. The second predicate: [@Day>=number(substring(now(),9,2))] returns a node set containing all birthdays whose Day field is greater than or equal to the current day of the month. This is one way you can perform a logical AND using XPath. You could also use the following expression:
count(//Birthdays[@Month=number(substring(now(),6,2)) and @Day>=
number(substring(now(),9,2))])
To display the next birthday this month (the day of this month the next birthday falls on), we use the following XPath expression:
string(ibmforms:current-table()[@Month=number(substring(now(),6,2))][@Day=
min(ibmforms:current-table()[@Month=number(substring(now(),6,2))][@Day>=
number(substring(now(),9,2))]/@Day)]/@Day)
We use the same predicates to identify birthdays for this month and after today; but we also use the min() function to determine which is the next day this month that a birthday will fall on. After we identify that node set, we use the string() function to convert that Day value into a string. If there are no more birthdays this month, this results in an empty string.
To display the number of birthdays that have already passed this year, we use the following XPath expression:
count(//Birthdays[@Month<number(substring(now(),6,2))]
| //Birthdays[@Month=number(substring(now(),6,2)) and @Day<number(substring(now(),9,2))])
We use the logical operators | and AND to combine the node sets of all birthdays that fall on the month less the current month and all birthdays that fall this month, but on days before today.
The All Birthdays grid provides a read-only display of all the birthday entries with drill-down functionality to open a selected entry in the Birthday Entry form. The columns in the grid display the full name of the person and the day and month on which he was born. In the data access designer, the form looks like Figure 9.
Figure 9. All Birthdays grid in the data access designer
To display the person's full name, we use the following XPath expression:
concat(@FirstName, ' ', @LastName)
To display the day and month, we use the following XPath expression:
concat(@Day, ' ', substring('JanFebMarAprMayJunJulAugSepOctNovDec',
((number(@Month)-1)*3)+1,3))
The month is displayed using a three-letter abbreviation, such as Jan for January. To calculate this, we use the date value to calculate the start index into a string literal containing all the abbreviations, and then use the substring() function to parse the correct abbreviation.
Data access tool XPath functions
The IBM data access tool supports all the XPath 1.0 and XForms 1.0 XPath functions as well as the following data access specific functions.
number ibmforms:record-index()
The ibmforms:record-index function returns the index of the current record.
number ibmforms:record-count()
The ibmforms:record-count function returns the count of records.
NOTE: Any records that are currently hidden or not included in the filtered set are not included in this count.
NodeSet ibmforms:current-record()
The ibmforms:current-record function returns a node set containing all of the values in the current record.
NodeSet ibmforms:current-table()
The ibmforms:current-table function returns a node set containing all of the values in the current table.
NOTE: All records are included in this node set even if they have been hidden or are not included in the filtered set.
The sample application presented in this article is intended to get you started in using XPath in the IBM data access tool. There are other XPath functions and expressions that you can use. These are fully documented as part of the XPath and XForms specifications. We recommend that you take a look at the developerWorks tutorial, "Get started with XPath," to get up to speed on the basic XPath syntax.
As you can see from the complexity of the XPath expressions, even doing simple things requires long expressions using XPath 1.0. The XQuery 1.0 and XPath 2.0 Functions and Operators working draft lists a number of functions that will significantly improve the ease of use of XPath.
If you encounter issues with the sample application, please email the author.
- The developerWorks: Lotus article, "What is the IBM data access tool?," describes what the data access tool is, how to access it, what types of design features it offers for designers, and what types of features it offers for users.
- Another developerWorks: Lotus article, "Creating a database application with the IBM data access tool," explains how you can use the data access tool to develop a simple call tracking database application.
- Learn more about XPath by visiting the following sites:
- XML Path Language (XPath) Version 1.0 Core Function Library
- XPaths Expressions in XForms Table of Contents
- XML Path Language (XPath) 2.0
- XQuery 1.0 and XPath 2.0 Functions and Operators working draft
- Get involved in the developerWorks community by participating in
developerWorks blogs.
-
Browse for books on these and other technical topics.
Mark Wallace is a software architect working in the Dublin Software Lab for IBM Ireland. He joined IBM Lotus in 1993 and since then has worked on a wide range of projects including Sametime Translation Services, Sametime Everyplace, and the IBM data access tool. His current role is technical lead for a team in Dublin who are contributing to the IBM Workplace Designer project.
Comments (Undergoing maintenance)





