

First Edition
Published December 2010

© Copyright IBM Corporation 2000, 2010
You can save a local copy of this document from your browser. Each browser has different menus and menu options. Consult the browser help if you need assistance saving the document locally.
If you would like to provide feedback about this document, see the Lotus Documentation Feedback Web site.
The IBM® Lotus® Forms Server – API provides low level access to XFDL forms in Java™, C, and COM.
Using the API, you can develop applications that process XFDL forms, including XForms data models within XFDL forms. You can create and manage applications that analyze, route, validate, and create electronic forms.
The API also contains the Function Call Interface (FCI) library. Using the FCI, you can extend the capabilities of your forms by adding custom XFDL functions that are available to forms designers for use at run time.
This version of Lotus Forms API has several updates, including performance improvements, additional platform support, and support of new features in XFDL 8.0.
Support for WebSphere® Application Server 7.0 and zLinux has been added. The support for i5/OS®, AIX®, and Linux® platforms has also been updated. See API system requirements for the complete list of currently supported platforms.
The API supports the latest version of XFDL markup.
Locale and time zone information in the API has been updated with version 4.4 of the International Components for Unicode (ICU) library.
The release notes provides a summary of new features and improvements, installation information, and descriptions of known limitations, problems, and workarounds.
| About this release | New features and improvements | What's new in IBM Lotus Forms Server – API 4.0 |
| Backward compatibility information | API system requirements | |
| Installation, migration, upgrade, and configuration information | System requirements | API system requirements |
| Installation instructions | Installing Lotus Forms API | |
| Known limitations, problems, and workaround | Troubleshooting | Troubleshooting and support |
| Limitations, problems, and workarounds | API technotes and flashes | |
| Contacting customer support | Customer support | Lotus Forms Support web page |
These procedures contain instructions for installing the API as a single component that is independent of other Lotus Forms Server products.
You can install the API using the installer in graphical mode or in console mode.
This procedure assumes that you will be using the graphical mode to install the API. To run the installer in console mode, at Step 2 open a terminal window and change to the temporary directory. If you are installing on a 32-bit computer, at the command line type LFServer_400_Win32.exe -i console. If you are installing on a 64-bit computer, at the command line type LFServer_400_Win64.exe -i console. The console procedure is almost identical except that you use the keyboard instead of the mouse to enter data and to navigate through the screens.
To install the API in graphical mode:
You can install the API using the installer in graphical mode or in console mode. To use the graphical installer, you must have X-Windows.
lslpp -La | grep -i xlC
On AIX 5.3, the version must be 8.0 or greater. On AIX 6.1 the version must be 9.0 or greater.
To download and install the latest version of the XL C runtime PTF for AIX, see http://www-01.ibm.com/support/docview.wss?uid=swg21215669
This procedure assumes that you will be using the graphical mode to install the API. The console procedure is almost identical except that you use the keyboard instead of the mouse to enter data and to navigate through the screens.
Table 1 shows the command that you issue in Step 2 to start the installer for your operating system.
| Operating system | 64-bit | 32-bit |
|---|---|---|
AIX |
./LFServer_400_AIXPPC64.bin |
./LFServer_400_AIXPPC32.bin |
IBM i (i5/OS) |
./LFServer_400_i5OS64.jar |
./LFServer_400_i5OS.jar |
Linux |
./LFServer_400_LinuxX64.bin |
./LFServer_400_Linux.bin |
zLinux |
./LFServer_400_LinuxS390x.bin |
./LFServer_400_LinuxS390.bin |
Solaris |
./LFServer_400_SolarisSparc64.bin |
./LFServer_400_SolarisSparc.bin |
To install the API in graphical mode:
You must add the location of the dynamic load libraries (DLLs) to the Windows PATH environment variable.
To set the Windows PATH environment variable for the API:
You must create the PureEdgeAPI.ini file if it does not exist. If the file already exists then you must update it to add the location of the new API.
The contents of the PureEdgeAPI.ini file determine which applications use which version of the API. The API will not work without a PureEdgeAPI.ini file.
To update the PureEdgeAPI.ini file:
[API] 8.0.* = C:\Program Files\IBM\Lotus Forms\Server\4.0\API\redist\Win32\PureEdge\80
8.0.* = C:\Program Files\IBM\Lotus Forms\Server\4.0\API\redist\Win32\PureEdge\80
The API can use a configuration file called prefs.config to set several properties. Normally, you do not need this file because the default settings are adequate for most purposes.
C:\Documents and Settings\<username>\Application Data\PureEdge\API 8.0\Prefs\prefs.config C:\Documents and Settings\All Users\Application Data\PureEdge\API 8.0\Prefs\prefs.config
When determining the configuration, the API first looks at the configuration file in the user specific folder, then the file in the All Users folder. The API will read both configuration files, but any settings in the user specific folder will override settings in the All Users folder.
The configuration file uses tag value pairs to set the following properties:
To configure the API, open the prefs.config file in a text editor and set the preferences accordingly. If you do not include a particular setting, the default is used instead. For example, your prefs.config file might look like this:
checkCRLDistributionPoints = off
In this case, the API will automatically detect both the location of the Netscape Profile and the location of the Java virtual machine. However, the API will not check CRL distribution points and will use the default configuration options for the Java VM.
You must add the location of the API library files to the library path for your operating system. You must also ensure that the location of the Network Security Services libraries is on your path.
If you are using AIX, the environment variable that you set is LIBPATH. If you are using Linux or Solaris, the environment variable that you set is LD_LIBRARY_PATH.
If you installed the API in the default location, the paths are as shown. Otherwise, you must set LIBPATH and LD_LIBRARY_PATH variables to match the location of the API on your system.
/usr/IBM/LotusForms/Server/4.0/API/redist/AIX /usr/IBM/LotusForms/Server/4.0/API/redist/AIX/PureEdge/80/system <path to Network Security Services libraries>
/opt/IBM/LotusForms/Server/4.0/API/redist/Linux /opt/IBM/LotusForms/Server/4.0/API/redist/Linux/PureEdge/80/system <path to Network Security Services libraries>
If you are using Solaris, set your LD_LIBRARY_PATH to the following directories:
/opt/IBM/LotusForms/Server/4.0/API/redist/SunOS /opt/IBM/LotusForms/Server/4.0/API/redist/SunOS/PureEdge/80/system <path to Network Security Services libraries>
The Network Security Services libraries are required for creating and verifying electronic signatures. The libraries are installed with most Netscape and Mozilla products, including Firefox, SeaMonkey, and Mozilla Application Suite. They can also be obtained directly from mozilla.org. To find the location of this directory on your computer, search for the file libnss3.so.
Many methods exist of setting these paths; the method that you choose depends on the operating system, the interactive shell that you use, the preferences of the system administrator, or any other policies that might apply in your particular environment.
You must create the PureEdgeAPI.ini file if it does not exist. If the file already exists then you must update it to add the location of the new API.
The contents of the PureEdgeAPI.ini file determine which applications use which version of the API. The API will not work without a PureEdgeAPI.ini file.
To update the PureEdgeAPI.ini file:
[API] 8.0.* = /usr/IBM/LotusForms/Server/4.0/API/redist/AIX/PureEdge/80
[API] 8.0.* = /opt/IBM/LotusForms/Server/4.0/API/redist/Linux/PureEdge/80
[API] 8.0.* = /opt/IBM/LotusForms/Server/4.0/API/redist/SunOS/PureEdge/80
The API can use a configuration file called prefs.config to set several properties. If you are using the API in Java, then you must set the location of the Java virtual machine. The default settings of the other properties are adequate for most purposes.
/home/<username>/.PureEdge/API 8.0/prefs/prefs.config /etc/PureEdge/API 8.0/prefs/prefs.config
When determining the configuration, the API first looks at the configuration file in the user-specific directory, then the file in the /etc/PureEdge/API 8.0/prefs directory. The API will read both configuration files, but any settings in the user-specific directory will override settings in the/etc/PureEdge/API 8.0/prefs directory.
After you have installed the API, create the prefs.config file if it does not already exist. The file uses tag value pairs to set the following properties:
| Tag | Setting |
|---|---|
overrideDefaultPathToNetscapeProfile |
The path to your Netscape certificate store. If the path is not provided, the API will attempt to locate the certificate store automatically. |
overrideDefaultPathToNetscapeSecurityLibraries |
The path to your Netscape security libraries. To determine this path, search for a directory that contains libnss3.so and libnspr4.so. |
rsaSecurityEngineNames |
A comma-separated list of security engines that the API will query for RSA signature support. The default setting in API version 3.5 and later is: rsaSecurityEngineNames = Netscape,CryptoAPI To query all signature engines that are supported by the API, use the * wildcard. For example: rsaSecurityEngineNames = * If you use the * wildcard, it must be the only entry. The * wildcard is the behavior of API versions 3.0.1 and earlier. If you are using the API on i5/OS, then you must either include the Java security engine in the list, or use the * wildcard. For example: rsaSecurityEngineNames = Netscape,CryptoAPI,Java If you specify an engine that is not supported on your platform, the API will ignore that entry. |
javaPath |
The path to your Java virtual machine. If you are using the API from a C program, the javaPath can point to any JVM on your system. If you are using the API from a Java program, the javaPath must point to the same JVM that the calling Java program uses. If the calling program is running in an application server such as WebSphere Application Server, or in a portal server such as WebSphere Portal, the javaPath must point to the same JVM that the application server uses. |
checkCRLDistribution Points |
An on or off value. If on, the API will check CRL distribution points whenever verifying a digital certificate. This requires Internet access, and will slow the verification process. If off, the API will only use locally stored CRLs to verify certificates. |
javaOptions |
A space delimited list of configuration options that will be passed to the Java VM when it is started by the API. By default, the API does not pass any configuration options. |
javaIgnoreUnrecognized |
An on or off value. If on, the Java VM will ignore unrecognized options used in the javaOptions setting. If off, the Java VM will report unrecognized options in the javaOptions setting. Default is off. |
domspooltodiskthreshold |
The in-memory size of form enclosures, in bytes. Enclosures that are greater than this threshold are removed from memory and spooled to a temporary file in the system temp folder. The default size is 32768 bytes. If you do not want enclosures to be spooled to disk, set the size to 0 (zero). Note that the size is the space that the enclosure takes when in memory; its file size might be different. |
schemasFolder |
Allows you to specify the path to the schemas folder for any external schemas required by an application or its forms. If no schemasFolder setting is specified, and an external schema is referenced, the API will look for a schemas folder in the same directory as the currently running application. |
defaultDateFormat |
Allows you to specify the format to assume for ambiguous dates that are entered by users. Valid settings are: DayMonthYear, MonthDayYear, YearMonthDay The default is MonthDayYear. For example, by default the date 01/02/09 will be interpreted as January 2, 2009. |
To configure the API, open the prefs.config file in a text editor and set the preferences accordingly. If you do not include a particular setting, the default is used instead. For example, your prefs.config file might look like this:
javaPath = /usr/java/jre/lib/sparc/libjava.so checkCRLDistributionPoints = off
In this case, the API will automatically detect the location of the Netscape Profile and will use the Java VM in the /usr/java/jre/lib/sparc/ directory. However, the API will not check CRL distribution points and will not send any configuration options to the Java VM.
You can deploy the API to WebSphere Application Server in one of two ways: automatically during installation, or manually after installation.
Several steps are required to manually deploy the API to WebSphere Application Server:
You must create environment variables for use in later configuration steps.
You cannot configure your system to use both the Classic and Streaming Java APIs. You must choose only one.
To set the WebSphere variables:
To prevent this problem: If you use a text editor to edit your forms, configure your editor so it does not add a BOM to UTF-8 documents. (See your text editor's documentation for details.)
To remove a BOM from a form: Open the form in the Designer and save it.
In cases in which you would normally use vbNull, you must instead use "1", which is the constant value of vbNull. If you do not use "1", the function you are calling will not work correctly.
The API includes the following sample applications:
These applications are provided to demonstrate the use of many of the functions in the API, and are useful for testing your API installation and development environment.
The Calculate Age sample application demonstrates the most commonly used functions in the API.
This application reads a form called CalculateAge.xfd into memory, and reads the values for the year, month, and day of birth from the form. It then writes those values into a series of hidden fields that are used by a compute to determine the person's age. Once the values are written, the application writes the form as Output.xfd.
The Form library sample application demonstrates more of the functions available in the API.
This application reads into memory an input form called formSample.xfd. It then reads the current date and birth date values from the form and sets a formula on the Age field that automatically computes the user’s age. Next, the application determines the user’s gender based on the radio buttons in the form. It then replaces these buttons with an image representing the user’s gender. Finally, it duplicates the address field to create a mailing address field. When complete, the application saves the changes made to formSample.xfd in a new form called output.xfd.
The API includes two different samples that demonstrate the FCI, depending on whether you are working in C or Java.
The FCI sample application for Java uses the FCI methods to create a function called convertDate. This function takes two arguments, the date and the locale, and converts the date into the specified country's language and format. The application displays the converted date as a value for the Converted Date label. In this sample, the JAR file containing the extension was embedded into the form.
The API includes the following sample applications:
The applications are provided to demonstrate the use of many of the functions in the API, and are useful for testing your API installation and development environment.
The Calculate Age sample application demonstrates the most commonly used functions in the API.
This application reads a form called calculateAge.xfd into memory, and reads the values for the year, month, and day of birth from the form. It then writes those values into a series of hidden fields that are used by a compute to determine the person's age. Once the values are written, the application writes the form as output.xfd.
The Form library sample application demonstrates more of the functions available in the API.
This application reads into memory an input form called formSample.xfd. It then reads the current date and birth date values from the form and sets a formula on the Age field that automatically computes the user's age. Next, the application determines the user's gender based on the radio buttons in the form. It then replaces these buttons with an image representing the user's gender. Finally, it duplicates the address field to create a mailing address field. When complete, the application saves the changes in a new form called output.xfd.
The API includes two different samples that demonstrate the FCI, depending on whether you are working in C or Java.
The FCI sample application for Java uses the FCI methods to create a function called convertDate. The convertDate function takes two arguments, the date and the locale, and converts the date into the language and format of the selected locale. The application displays the converted date as a value for the Converted Date label.
The JSP sample application is only available for Java. The sample demonstrates how to integrate XFDL code with JSP. This application consists of three files: jspget.jsp, getlabel.txt, and jsppost.txt. To run the application, you need to place the above files into a J2EE compliant web server's JSP folder. For more information refer to Lotus Forms Server API – Java API User's Manual and to your web server's documentation.
Java Server Pages (JSP) is a platform independent technology designed to make it easier to add dynamic content to web pages. A web application server compiles JSP scripts into Java servlets that are executed by the server's Java virtual machine. The resulting dynamic content is inserted into the web document that is then displayed in the end user's web browser. Because JSP technology integrates with both HTML and XML documents, you can use JSP to extend the capabilities of XFDL forms.
This section assumes that you are familiar with both JSP and XFDL. The information in this section is not intended to show you how to write JSP scripts. Rather, its purpose is to explain how to integrate XFDL with JSP. For information on JSP refer to http://java.sun.com/products/jsp/. For more information on XFDL, refer to the XFDL Specification.
To process JSP pages that contain XFDL, you must have one of the following servlet containers:
Creating a JSP page involves integrating JSP elements with the source code of the original HTML or XML web document. In the case of XFDL forms, this means adding appropriate JSP elements to the form's XFDL code using a text editor. Once complete, the resulting document is a JSP page and should have a .jsp extension.
While the specific content of each JSP page depends on the logic of the application and the design of the form, certain fixed elements must be present in every JSP page that contains XFDL. The following elements must appear exactly as shown in every JSP that includes XFDL:
| Element | Description |
|---|---|
| <?xml version="1.0"?> | This line is the standard XML file identifier. It must appear as the first line in the JSP page. There can be no blank lines or spaces ahead of this text. |
| <% response.setContentType("application/x-xfdl"); %> | This line sets the mime type of the http response object. In this case, it identifies the object as an XFDL document. As a result, the user's browser will display the document using the Viewer. With the exception of any optional comments or whitespace, this line should appear immediately after the XML file identifier. |
Once you have included these standard elements, you can add the rest of your custom JSP code such as directives, declarations, or scriptlets.
The following source code creates a simple JSP page containing an XFDL form. In this example, the JSP scriptlet obtains the current date and converts it into a string. The form contains an XFDL label item that displays the string.
<?xml version="1.0"?>
<%-- Set the content-type so that the Web browser uses the
Viewer to display the XFDL form. --%>
<% response.setContentType("application/x-xfdl"); %>
<%@ page import="java.util.Date" %>
<%-- This is the JSP scriptlet --%>
<%
Date theDate = new Date();
String theDateString = theDate.toString();
%>
<%-- The following XFDL code defines the form --%>
<XFDL xmlns:custom="http://www.ibm.com/xmlns/prod/XFDL/Custom"
xmlns:designer="http://www.ibm.com/xmlns/prod/workplace/forms/designer/2.6"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:xfdl="http://www.ibm.com/xmlns/prod/XFDL/7.6"
xmlns:xforms="http://www.w3.org/2002/xforms"
xmlns="http://www.ibm.com/xmlns/prod/XFDL/7.6"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<globalpage sid="global">
<global sid="global">
<designer:date>20100129</designer:date>
<formid>
<title></title>
<serialnumber>9710555EEBA829D8:-11895BD9:1267B9B3652:-8000</serialnumber>
<version>1.0.0</version>
</formid>
<history>
<editors>
<editor minversion="3.5" version="3.5.1">IBM Lotus Forms Designer</editor>
</editors>
</history>
</global>
</globalpage>
<page sid="PAGE1">
<global sid="global">
<label>PAGE1</label>
</global>
<label sid="LABEL1">
<itemlocation>
<x>15</x>
<y>15</y>
</itemlocation>
<value><%= theDateString%></value>
</label>
<spacer sid="vfd_spacer">
<itemlocation>
<x>960</x>
<y>1260</y>
<width>1</width>
<height>1</height>
</itemlocation>
</spacer>
</page>
</XFDL>
The API includes a sample web application demonstrating how to use JSP pages to extend the functionality of XFDL forms. This simple application consists of three files, located in the folder <API Program Folder>\samples\java\jsp\demo. The following table describes the functions of each file:
| File | Description |
|---|---|
| jspget.jsp | This JSP page is an example of a standard HTTP GET operation. The first JSP scriptlet obtains the current date and calculates the number of days until Christmas. The XFDL form specifies two labels to display this information. The second scriptlet creates the URL that the form's submit button uses to call jsppostt2.jsp. Within the XFDL portion of the page, a JSP include directive obtains a label item from getlabel.txt. |
| getlabel.txt | Although this file does not contain a complete form, it does contain XFDL code defining a label item. jspget.jsp accesses this code using an include directive. |
| jsppost.jsp | This page is an example of a standard HTTP POST operation. The scriptlet uses the API's streaming method readForm to obtain the number of days until Christmas and the signature from jspget.jsp. It then uses getSignatureVerificationStatus to validate the signature. Finally, the XFDL portion of the page specifies two labels to display the results to the end user. |
To run this sample in WebSphere Application Server, you must first package the above files into a WAR file. You can then deploy the WAR file using the WebSphere Application Server Administrator Console. For other application servers, you can package the files as WARs or place the JSP files into your web server JSP folder. Refer to the documentation of your application server for more information.
To run the application, users either follow a link or type the URL into the address field of the browser. For example, if the application is running on WebSphere Application Server, the URL would be: http://<server_name>:<port><example_dir>/jsp/jspget.jsp.
The tutorials demonstrate how to work with XFDL forms in C, Java™, and Visual Basic. They also demonstrate the features of the API that allow you to create, modify, and save XFDL forms, and how to extend XFDL forms using the function call interface library.
Learn how to manipulate and extend forms in Java with IBM Lotus Forms Server - Java API.
In this tutorial, you will create an application that shows you how to read, modify, and write forms using the Form Library
By working through the tutorial, you will perform all of the steps involved in creating a simple application that uses the API methods, including:
The sample application in this tutorial reads an input form called CalculateAge.xfd into memory. It retrieves the user's birth day, month, and year as well as the current date from the form. It then places these values into hidden fields in the form. This triggers the form to compute the user's age and display the result. When complete, the application saves the changes made to calculateAge.xfd as a new form called Output.xfd.
The tutorial describes the following tasks:
import com.PureEdge.DTK; import com.PureEdge.xfdl.FormNodeP; import com.PureEdge.xfdl.XFDL; import com.PureEdge.error.UWIException; import com.PureEdge.IFSSingleton;
public class CalculateAge
{
private static FormNodeP theForm;
public static void main(String argv[])
{
int birthYear;
int birthMonth;
int birthDay;
try
{
initialize();
loadForm();
birthYear = getBirthYear();
birthMonth = getBirthMonth();
birthDay = getBirthDay();
setBirthYear(birthYear);
setBirthMonth(birthMonth);
setBirthDay(birthDay);
saveForm();
theForm.destroy();
}
catch (Exception ex)
{
ex.printStackTrace();
}
/* Additional code removed */
}
}
All applications that use the API functions must initialize the Form Library to ensure correct error and memory handling behavior. The sample application does this in a separate method calledinitialize. In turn, initialize calls the Form Library method DTK.initialize and passes it the name of the current program.
32-bit applications that use methods from the Form Library will run on any computer that supports the Java Runtime Environment or the Microsoft Software Development Kit For Java v3.1 or later.
If you distribute applications that use the Form Library, you will also need to distribute a number of API files. Refer to Developing and distributing applications for information about distributing applications that use the Form Library.
By working through this section you have successfully built the Calculate Age application. In the process, you have learned how to initialize, compile, and test form applications using the following methods from the Form Library:
The source code for the Calculate Age application is included with this API and can be found in the following folder:
<API Program folder>\Samples\Java\Form\Demo\Calculate_Age\
<API Program folder>\Samples\Java\Form\Demo\Sample_Application\
To view the forms provided with the sample applications, you must have a copy of the Viewer installed.
In this tutorial, you will create a simple extension to an XFDL form.
A series of practical examples is provided which you can work through to build a package of functions called sample_package, which contains one function called convertDate. The convertDate function converts a date to a language and format specific to another country. Try adding other functions to the package for more practice using the FCI Library of methods.
Although the FCI Library contains many methods, you only need to use a few of them to create a simple package of functions. These are:
The remaining FCI methods allow you to customize the behavior of your functions and extensions. For example, you can attach additional information to a particular extension, or get a list of currently registered extensions.
The following table is a guide for creating extensions using the Function Call Interface. Refer to the corresponding page numbers for more details:
| Procedure | Page |
|---|---|
| Install Lotus Forms Server - API and related files, as outlined in Installing Lotus Forms API. | N/A |
| Set up the extension. | Creating the Extension class |
| Create the Extension class. | Creating the Extension class |
| Create the extension initialization method. | Implementing the extension initialization method |
| Create a new FunctionCall object. | Creating a new FunctionCall object |
| Set up the function call. | Creating a FunctionCall class |
| Create the FunctionCall class. | Creating a FunctionCall class |
| Retrieve the Function Call Manager. | Retrieving the Function Call Manager |
| Register each FunctionCall object with the IFX Manager. | Registering the FunctionCall object with the IFX Manager |
| Register your package(s) of custom functions with the Function Call Manager. | Registering your packages of custom functions with the Function Call Manager |
| Implement your custom functions. | Implementing your custom functions |
| Provide help information for each of your functions. | Providing help information for each of your functions |
| Build the extension. | Building the extension |
| Distribute the extension for Testing or Use. | Distributing Extensions for Testing or Use |
When the Forms System is initialized, the API checks for existing extensions and calls the initialization method (extensionInit) for each extension. Your first step in creating a function call is to create an Extension class that generates a new FunctionCall object. Follow the procedure below to create the Extension class called FCIExtension:
The API will initialize an extension by calling the extensionInit method and passing the method an object known as the IFX Manager.
public class FCIExtension extends ExtensionImplBase implements
Extension
{
public void extensionInit(IFX IFXMan) throws UWIException
{
/* Additional code removed */
}
}
The extensionInit method creates a new FunctionCall object that contains your custom-built functions.
To create a new FunctionCall object you must define a FunctionCall class that contains your custom functions. Refer to "Setting up the FunctionCall Class" for more details.
Each FunctionCall object registers itself with the IFX Manager as an interface that provides function call support.
public FciFunctionCall(IFX IFXMan) throws UWIException
{
FunctionCallManager theFCM;
if ((theFCM = IFSSingleton.getFunctionCallManager()) == null)
throw new UWIException("Needed Function Call Manager");
IFXMan.registerInterface(this,
FunctionCall.FUNCTIONCALL_INTERFACE_NAME,
FunctionCall.FUNCTIONCALL_CURRENT_VERSION,
FunctionCall.FUNCTIONCALL_MIN_VERSION_SUPPORTED,
0x01000300, 0, null, theFCM.getDefaultListener( ));
}
public FciFunctionCall(IFX IFXMan) throws UWIException
{
FunctionCallManager theFCM;
if ((theFCM = IFSSingleton.getFunctionCallManager()) == null)
throw new UWIException("Needed Function Call Manager");
IFXMan.registerInterface(this,
FunctionCall.FUNCTIONCALL_INTERFACE_NAME,
FunctionCall.FUNCTIONCALL_CURRENT_VERSION,
FunctionCall.FUNCTIONCALL_MIN_VERSION_SUPPORTED,
0x01000300, 0, null, theFCM.getDefaultListener( ));
theFCM.registerFunctionCall(this, "sample_package",
"convertDate", FciFunctionCall.CONVERTDATE,
FunctionCall.FCI_FOLLOWS_STRICT_CALLING_PARAMETERS,
"S,S", 0x01000300, "Converts a date to a different
locale");
}
Along with registering your package(s) of custom functions with the Function Call Manager, the registerFunctionCall method is also used to specify a version number for each function that you create. In the previous example, the ConvertDate function is registered with the version number 0x01000300.
Assigning a version number to each function allows you to provide upgrades to single functions in extensions you have already distributed to users.
For example, if you distributed an extension containing a package of 50 functions for your application and then wanted to change the behavior of one of the functions, you could:
When the API initializes all of the extensions it would find two functions with the same package name and function name. It would deregister the one with the lower version number thereby updating your application.
The main purpose of package names is to distinguish the functions in a package from those in other packages that could potentially have the same names. All packages you create must contain an underscore in their names. For example, the convertDate function belongs to a package called sample_package.
public class FciFunctionCall extends FunctionCallImplBase implements
FunctionCall
{
/* Additional Code Removed */
public void evaluate(String thePackageName,
String theFunctionName, int theFunctionID,
int theFunctionInstance, short theCommand,
com.PureEdge.xfdl.FormNodeP theForm,
com.PureEdge.xfdl.FormNodeP theComputeNode,
com.PureEdge.IFSUserDataHolder theFunctionData,
com.PureEdge.IFSUserDataHolder theFunctionInstanceData,
com.PureEdge.xfdl.FormNodeP [] theArgList,
com.PureEdge.xfdl.FormNodeP theResult) throws UWIException
{
String theDateString;
String theLocaleString;
String theAnswerString = null;
Date theDate = null;
Locale theLocale;
DateFormat theDateFormat;
if (theCommand == FunctionCall.FCICOMMAND_RUN)
{
/* Now we'll switch on the function ID. This makes it easy for a
single FunctionCall object to support multiple functions. */
if (theFunctionID == FciFunctionCall.CONVERTDATE)
{
/* First, we'll grab the string values of the two arguments.
Since we indicated that this method has two parameters and
that it must have two parameters
(FCI_FOLLOWS_STRICT_CALLING_PARAMETERS) when we registered
it, we don't have to check to see if we actually received
both parameters, since this code won't even be called unless
the caller used the right number of parameters. */
theDateString = theArgList[0].getLiteralEx(null);
theLocaleString = theArgList[1].getLiteralEx(null);
/* Now we perform the conversion. */
if (theLocaleString.length( ) != 5)
theAnswerString = "Locale must be 2 characters, " +
"a space and 2 characters";
else
{
theLocale = new Locale(theLocaleString.substring(0, 2),
theLocaleString.substring(3));
if ((theDateFormat = DateFormat.getDateInstance(
DateFormat.LONG, theLocale)) == null)
theAnswerString = "Unrecognized locale";
else
{
try
{
if ((theDate = new SimpleDateFormat
("yyyyMMdd").parse(theDateString)) == null)
theAnswerString = "Unable to parse";
}
catch (ParseException ex)
{
theAnswerString = ex.toString( );
}
if (theAnswerString == null)
theAnswerString = theDateFormat.format(theDate);
}
}
/* Lastly, we'll store the result in the result node */
theResult.setLiteralEx(null, theAnswerString);
}
}
/* Additional Code Removed */
}
By using the method help, you can provide help information to form designers within a development environment (for example, Lotus Forms Designer). Use help to help form designers choose and use the correct functions.
public class FciFunctionCall extends
com.PureEdge.xfdl.FunctionCallImplBase implements FunctionCall
{
/* Additional Code Removed */
public void help(String thePackageName,
String theFunctionName, int theFunctionID,
com.PureEdge.IFSUserDataHolder theFunctionData,
com.PureEdge.StringHolder theQuickDesc,
com.PureEdge.StringHolder theFunctionDesc,
com.PureEdge.StringHolder theSampleCode,
com.PureEdge.StringListHolder theArgsNameList,
com.PureEdge.StringListHolder theArgsDescList,
com.PureEdge.ShortListHolder theArgsFlagList,
com.PureEdge.StringHolder theRetValDesc,
com.PureEdge.ShortHolder theRetValFlag) throws
UWIException
{
switch(theFunctionID)
{
case FciFunctionCall.CONVERTDATE:
theQuickDesc.value = "Converts a date to a different " +
"locale";
theFunctionDesc.value = "This function takes a date in " +
"the first parameter and a locale in the second " +
"parameter and returns the date formatted for the " +
"specified locale";
theSampleCode.value = "\t <LABEL SID = \"LABEL1\"> \n" +
"\t\t <VALUE COMPUTE = "sample_package.convert " +
"(\'19980101\', \'french\')"></VALUE> \n" +
"\t\t <SIZE CONTENT = \"ARRAY\"> \n" +
"\t\t\t <AE>10</AE> \n" +
"\t\t\t <AE>1</AE> \n" +
"\t\t </SIZE> \n" +
"\t </LABEL> \n";
theArgsNameList.value = new String[2];
theArgsNameList.value[0] = "theDate";
theArgsNameList.value[1] = "theLocale";
theArgsDescList.value = new String[2];
theArgsDescList.value[0] = "The english date";
theArgsDescList.value[1] = "The locale";
theRetValDesc.value = "The formatted date";
break;
}
}
}
Once you have generated the Java source files for your Extension class, you must compile the source code to create the extension.

Once you have created your extensions you can package them for testing or distribution by using either of the following methods:
Once you have created your extensions you can package the .class files into a single Java Archive (JAR) file and distribute the file. This means that you can package multiple extensions into one JAR file for distribution.
Before building the JAR file, you must create a manifest that indicates which classes in the JAR file are extensions.
Once you have packaged your extension, you can install it for testing or distribute it for general use. In either case, you place the extension in the same location.
To distribute your extensions so that they may be used by a specific Lotus Forms product:
To distribute your extensions so that they may be used by all Lotus Forms products:
JAR files that are enclosed in forms must have the MIME type set to:
The corresponding XFDL code will look like this:
<mimetype>
application/uwi-jar
</mimetype>
If you are using the Designer to enclose a JAR file into a form the Designer will set the MIME type for you.
You can also override the default Java Virtual Machine (JVM) that is used to run the enclosed JAR file. To do this, you must add a vm parameter to your MIME type, as shown:
<mimetype>
application/uwi-jar; vm="<java virtual machine>"
</mimetype>
Set this to the string that the JVM uses to register itself. For example,"Sun VM <version>". The version is guaranteed to include a major and minor number, and may include further information, such as a maintenance number, build number, and so on.
Since it can be difficult to get an exact match, you can use the * wildcard in the version string. For example, you might use the following string:
Sun VM 1.4*
This will match any version beginning with 1.4, such as “1.4.2_03 JDK” or “1.4 JDK”.
When there are multiple matches, the API will default to the latest version. For example, if you search for version 1.4*, and you have “Sun VM 1.4 JDK” and “Sun VM 1.4.2_03 JDK” installed, the API will use version 1.4.2_03.
Additionally, the API always chooses the JDK over the JRE. For example, if you search for version 1.4*, and you have “Sun VM 1.4.2_03 JDK” and “Sun VM 1.4.2_03 JVM” installed, the API will use the JDK.
The location in which an extension is installed determines how much access the extension has to the user's system resources (for example, user's system files). The following table summarizes the security features that are set when an extension is installed in a particular location.
Note that the following methods are not available to extensions that are enclosed within an XFDL form:
In addition extensions that are enclosed within a form cannot execute the following functions on a local computer:
By working through this section you have successfully built an extension for date conversion. In the process you have learned how to set up, compile, test, and distribute extensions. You also learned how to use the following FCI methods:
The Convert Date sample application is included with this API. you will find it in the folder < Program Folder>\samples\java\fci\demo\convert_date.
In order to view the forms provided with this sample application, you must have a licensed or evaluation copy of Lotus Forms Viewer installed.
Learn how to manipulate and extend forms in C with IBM Lotus Forms Server - C API.
In this tutorial, you will create an application that shows you how to read, modify, and write forms using the Form Library
By working through the tutorial, you will perform all of the steps involved in creating a simple application that uses the API functions, including:
The sample application in this tutorial reads an input form called CalculateAge.xfd into memory. It retrieves the user's birth day, month, and year as well as the current date from the form. It then places these values into hidden fields in the form. This triggers the form to compute the user's age and display the result. When complete, the application saves the changes made to calculateAge.xfd as a new form called Output.xfd.
The tutorial describes the following tasks:
All applications that use the API functions must initialize the Form Library to ensure correct error and memory handling behavior. The sample application does this in a separate method calledinitialize. In turn, initialize calls the Form Library function IFSInitialize and passes it the name of the current program.
#ifndef OLD_STYLE_PARAMS
r_short initialize(void *theInstance)
#else
r_short initialize(theInstance)
void *theInstance;
#endif
{
r_error error;
/* Call IFSInitialize. The parameters are:
1. SampleAp : the name of the application being run.
2. 1.0.0 : the version of the application being run.
3. 2.6.0 : the version of the API to use by default.
An error code will be returned if there is a problem. */
error =IFSInitialize("SampleAp", "1.0.0", "2.6.0");
if (error != OK)
{
reportError("IFSInitialize error %hd.\n", (r_short)error);
return(NOTOK);
}
return(OK);
}
Before your program can begin working with a form, you must load it into memory. CalculateAge does this by defining a loadForm function to handle these tasks.
#ifndef OLD_STYLE_PARAMS
r_short loadForm(formNodeP *form)
#else
r_short loadForm(form)
formNodeP *form;
#endif
{
/* Call UFLReadForm. The parameters are:
1. calculateAge.xfd : indicates the file on the local drive to read the
form from.
2. 0 : because we do not need the #include lines to be resolved.
The UFLReadForm will return a pointer to the root node of the new form
structure once it is loaded into memory. */
if ((*form = UFLReadForm("calculateAge.xfd", 0)) == NULL)
{
reportError("Could not load form.\n", 0);
return(NOTOK);
}
return(OK);
}
Once you have set up and initialized your application with the API and loaded a form into memory, your application is ready to start working with the form. The following code uses UFLGetLiteralByRefEx to get a specific value from the form:
#ifndef OLD_STYLE_PARAMS
r_short getBirthDate(formNodeP form, int *birYear, *birMonth,
int *birDay)
#else
r_short getBirthDate(form, birYear, birMonth, birDay)
formNodeP form;
int *birYear;
int *birMonth;
int *birDay;
#endif
{
r_charP temp=NULL;
r_short error;
/* Call UFLGetLiteralByRefEx to get the literal information for the
'PAGE1.BIRTHYEAR.value' node. An error code will be returned if there
is a problem. */
error = UFLGetLiteralByRefEx(form, NULL, "PAGE1.BIRTHYEAR.value", 0,
NULL, NULL, &temp);
if (error != OK)
{
reportError("UFLGetLiteralByRefEx error %hd.\n", error);
return(NOTOK);
}
/* If a literal value was returned, convert it into an integer value.
Otherwise, indicate that no value was entered into the field, and exit
the program. */
if (temp != NULL)
*birYear = atoi((char *)temp);
else
{
reportError("The birth year was not entered.\n",0 );
return(NOTOK);
}
/* Call UFLGetLiteralByRefEx to get the literal information for the
'PAGE1.BIRTHMONTH.value' node. An error code will be returned if there
is a problem. */
error = UFLGetLiteralByRefEx (form, NULL, "PAGE1.BIRTHMONTH.value",
0, NULL, NULL, &temp);
if (error != OK)
{
reportError("UFLGetLiteralByRefEx error %hd.\n", error);
return(NOTOK);
}
/* If a literal value was returned, convert it into an integer value.
Otherwise, indicate that no value was entered into the field, and exit
the program. */
if (temp != NULL)
*birMonth = atoi((char *)temp);
else
{
reportError("The birth month was not entered.\n",0 );
return(NOTOK);
}
/* Call UFLGetLiteralByRefEx to get the literal information for the
'PAGE1.BIRTHDAY.value' node. An error code will be returned if there is
a problem. */
error = UFLGetLiteralByRefEx(form, NULL, "PAGE1.BIRTHDAY.value", 0,
NULL, NULL, &temp);
if (error != OK)
{
reportError("UFLGetLiteralByRefEx error %hd.\n", error);
return(NOTOK);
}
/* If a literal value was returned, convert it into an integer value.
Otherwise, indicate that no value was entered into the field, and exit
the program. */
if (temp != NULL)
*birDay = atoi((char *)temp);
else
{
reportError("The birth day was not entered.\n", 0);
return(NOTOK);
}
return(OK);
}
Once a form is loaded into memory, a developer can set the values associated with any of the item or option nodes located in the form by calling UFLSetLiteralByRefEx.
#ifndef OLD_STYLE_PARAMS
r_short setBirthDate(formNodeP form, int birYear, int birMonth,
int birDay)
#else
r_short setBirthDate(form, birYear, birMonth, birDay)
formNodeP form;
int birYear;
int birMonth;
int birDay;
#endif
{
r_short error;
char *temp[100];
/* Convert the int to a r_charP and set the value of PAGE1.HIDDENYEAR.value */
sprintf(temp, "%d", birYear);
/* Call UFLSetLiteralByRefEx. The parameters are:
1. form : the formNodeP to use as the starting point for the search.
2. XFDL : the scheme used to write the reference parameter.
3. PAGE1.HIDDENYEAR.value : the reference to the node.
3. 0 : must be zero.
4. NULL : use the ANSI character set.
5. NULL : do not use a namespace node.
6. temp : the value to assign to the literal.
An error is returned if there is a problem. */
error = UFLSetLiteralByRefEx(form, NULL, "PAGE1.HIDDENYEAR.value",
0, NULL, NULL, (r_charP)temp);
if(error!= OK)
{
reportError("UFLSetLiteralByRefEx error%hd.\n", error);
return(NOTOK);
}
/* Convert the int to an r_charP and set the value of
PAGE1.HIDDENMONTH.value*/
sprintf(temp, "%d", birMonth);
error = UFLSetLiteralByRefEx(form, NULL, "PAGE1.HIDDENMONTH.value",
0, NULL, NULL, (r_charP)temp);
if(error!= OK)
{
reportError("UFLSetLiteralByRefEx error%hd.\n", error);
return(NOTOK);
}
/* Convert the int to an r_charP and set the value of PAGE1.HIDDENDAY.value */
sprintf(temp, "%d", birDay);
error = UFLSetLiteralByRefEx(form, NULL, "PAGE1.HIDDENDAY.value", 0,
NULL, NULL, (r_charP)temp);
if(error!= OK)
{
reportError("UFLSetLiteralByRefEx error%hd.\n", error);
return(NOTOK);
}
return(OK);
}
Once you have finished making the desired changes to the form, you should save it to disk. If you want to retain the original form (calculateAge.xfd), you should save the modified form under a new name. This program saves the modified form as Output.xfd.
#ifndef OLD_STYLE_PARAMS
r_short saveForm(formNodeP form)
#else
r_short saveForm(form)
formNodeP form;
#endif
{
r_short error;
/* Call UFLWriteForm. The parameters are:
1. form : a pointer to the root node of the form.
2. output.xfd : the filename you want to use (you could also use a path
here).
3. NULL : since we do not want to set a triggeritem.
4. 0 : since we do not want to allow the transmit options to work.
An error code is returned if there is a problem. */
error = UFLWriteForm (form, "output.xfd", NULL, 0);
if (error != OK)
{
reportError("UFLWriteForm error %hd.\n", error);
return(NOTOK);
}
return(OK);
}
Next, you must free the memory used by the form. This is the last operation in the main function of the program.
/* Now that we are done with the form, we can free the memory by
calling UFLDestroy. The parameter, 'theForm', is a pointer to
the root node of the form. This causes the root node and all
of its children (the complete form) to be deleted from memory. */
UFLDestroy(theForm);
return(OK);
}
If you distribute applications that use the Form Library, you will also need to distribute a number of API files. Refer to Developing and distributing applications for information about distributing applications that use the Form Library.
By working through this section you have successfully built the Calculate Age application. In the process, you have learned how to initialize, compile, and test form applications using the following methods from the Form Library:
The source code for the Calculate Age application is included with this API and can be found in the following folder:
<API Program folder>\Samples\Win32\Form\Demo\Calculate_Age\
<API Program folder>\Samples\Win32\Form\Demo\Sample_Application\
To view the forms provided with the sample applications, you must have a copy of the Viewer installed.
In this tutorial, you will create a simple extension that adds a multiply function to an XFDL form.
A series of practical examples is provided which you may work through to build an extension called Sample Extension. The extension contains a package of functions called sample_package, which contains one function called multiply. The multiply function multiplies the value of two fields together and stores the result in a third field. Try adding other functions to the package for more practice using the FCI Library.
Although the FCI Library contains many functions, you only need to use a few of them to create a simple package of functions. These are:
The remaining FCI functions allow you to customize the behavior of your functions and extensions. For example you can attach additional information to a particular extension, or get a list of currently registered extensions.
The following table is a guide for creating extensions using the FCI Library of functions. Refer to the corresponding page numbers for more details:
| Procedure | Page |
|---|---|
| Install the API and related files, as outlined in the IBM Lotus Forms Server - API Installation and Setup Guide. | N/A |
| Set up the extension. | Setting Up the Extension |
| Create the extension source file. | Creating the Extension Source File |
| Set up the extension initialization function. | Setting Up the Extension Initialization Function |
| Create C_ExtensionInit. | Creating C_ExtensionInit |
| Create a new Function Call structure. | Creating a New FunctionCall Structure |
| Define the services of the Function Call structure. | Defining Services Provided by the FunctionCall Structure |
| Register each Function Call structure with the IFX Manager. | Registering a FunctionCall with the IFX Manager |
| Register your package(s) of custom functions with the Forms System. | Registering your packages of custom functions using the Function Call Manager |
| Implement your custom functions. | Implementing your custom functions |
| Provide help information for each of your functions. | Providing help information for each of your functions |
| Compile the extension. | Compiling Your Extension |
| Test the extension. | Testing the Extension |
| Distribute the extension. | Distributing Extensions |
When the Forms System is initialized, the API checks for any existing extensions and calls the initialization function (C_ExtensionInit). Your first step in creating a custom function is to set up and initialize C_ExtensionInit.
The API will initialize an extension by calling the function C_ExtensionInit and passing it a pointer to the extension and a pointer to the IFX Manager.
#ifndef OLD_STYLE_PARAMS
PRE_FUNCTION_DECL r_short POST_FUNCTION_DECL C_ExtensionInit(
Extension *theExtension, IFX *theIFXManager)
#else
PRE_FUNCTION_DECL r_short POST_FUNCTION_DECL C_ExtensionInit(
theExtension, theIFXManager)
Extension *theExtension;
IFX *theIFXManager;
#endif
{
/* Additional code removed */
}
The FunctionCall structure you have created will provide two services or functions.
The first service is the implementation of your custom functions. A forward declaration for this service was declared previously as the function FCISimpleFunctions. This function tells the forms driver what to do when custom functions are called from within forms.
The second service defines the help available to form designers when working with your custom functions in Lotus Forms compliant products, such as the Designer. A forward declaration for this service was declared previously as the function FCISimpleHelp.
if ((theError = FunctionCall_SetObjectProc(theFunctionCall, (voidP)FCISimpleFunctions, FUNCTIONCALLEVALUATE)) != OK) return(theError); if ((theError = FunctionCall_SetObjectProc(theFunctionCall, (voidP)FCISimpleHelp, FUNCTIONCALLHELP)) != OK) return(theError);
Each FunctionCall structure must be registered with the IFX Manager as an interface that provides function call support.
/* Get the default listener */
if ((returnPtr = FCMGetDefaultListener()) == NULL)
return(NOTOK);
/* Register the interface with the Extention Manager.*/
if ((theError = IFXRegisterInterface(theIFXManager,
(GenericInterface*)theFunctionCall, FUNCTIONCALL_INTERFACE_NAME,
FUNCTIONCALL_CURRENT_VERSION, FUNCTIONCALL_MIN_VERSION_SUPPORTED,
0X01000300L, 0, NULL, 0, returnPtr)) != OK)
return(theError);
if ((theError = FCMRegisterFunctionCall(theFunctionCall, PACKAGE_NAME,
"multiply", FCI_MULTIPLY_ID, FCI_FOLLOWS_STRICT_CALLING_PARAMETERS,
"S,S", FCI_MULTIPLY_VERSION, FCI_MULTIPLY_DESCRIPTION)) != OK)
return(theError);
/* Finished */
return(OK);
}
Along with registering your package(s) of custom functions with the Function Call Manager, the FCMRegisterFunctionCall function is also used to specify a version number for each function that you create. In the previous example the multiply function is registered with the version number 0x01000300.
Assigning a version number to each function allows you to provide upgrades to single functions in extensions you have already distributed to users.
For example, if you distributed an extension containing a package of 50 functions for your application and then wanted to change the behavior of one of the functions you could:
When the API initialized all of the extensions it would find two functions with the same package name and function name. It would deregister the one with the lower version number thereby updating your application.
The main purpose of package names is to distinguish the functions in a package from those in other packages that could potentially have the same names. All packages you create must contain an underscore in their names. For example the beep function belongs to a package called my_funcs.
#ifndef OLD_STYLE_PARAMS
PRE_FUNCTION_DECL r_short POST_FUNCTION_DECL FCISimpleFunctions(FunctionCall
*theObject,
r_charP thePackageName, r_charP theFunctionName, r_u_long theFunctionID,
r_long theFunctionInstance,
r_short theCommand, formNodeP theForm, formNodeP theComputeNode, IFSUserData
**theFunctionDataPtr, IFSUserData **theFunctionInstanceDataPtr, formNodeP
*theArgList, r_long
theArgListSize, formNodeP theResult)
#else
PRE_FUNCTION_DECL r_short POST_FUNCTION_DECL FCISimpleFunctions(theObject,
thePackageName, theFunctionName,theFunctionID, theFunctionInstance,theCommand,
theForm,
theComputeNode, theArgList, theArgListSize, theResult)
FunctionCallManager *theObject;
r_charP thePackageName;
r_charP theFunctionName;
r_u_long theFunctionID;
r_long theFunctionInstance;
r_short theCommand;
formNodeP theForm;
formNodeP theComputeNode;
IFSUserData **theFunctionDataPtr;
IFSUserData **theFunctionInstanceDataPtr;
formNodeP *theArgList;
r_long *theArgListSize;
formNodeP theResult;
#endif
{
r_charP theFirstParam;
double firstNum;
r_charP theSecondParam;
double secondNum;
r_short theError;
double theMultiplyResult;
char theLiteralBuffer[50];
/* Verify the input parameters */
if ((thePackageName == NULL) || (theFunctionName == NULL))
{
MessageBox(NULL, "Invalid parameters", "Error", MB_OK);
return(NOTOK);
}
/* Make sure that the package is the one we are expecting. */
if (cp_strcmp(thePackageName, PACKAGE_NAME) != OK)
{
MessageBox(NULL, "This function was called with the wrong
package name", "Error", MB_OK);
return(NOTOK);
}
/* First switch based on the command */
switch (theCommand)
{
case FCICOMMAND_RUN:
/* Now switch based on the function name */
switch (theFunctionID)
{
/* The "multiply" function. */
case FCI_MULTIPLY_ID:
if ((theError = UFLGetLiteralEx(theArgList[0],
NULL, &theFirstParam)) != OK)
{
MessageBox(NULL, "Unable to get the first
parameter.", "Error", MB_OK);
return(theError);
}
if ((theError = UFLGetLiteralEx(theArgList[1],
NULL, &theSecondParam)) != OK)
{
MessageBox(NULL, "Unable to get the first
parameter.", "Error", MB_OK);
return(theError);
}
/* Since the literal can be NULL, we make sure that it isn't */
if (theFirstParam == NULL)
theFirstParam = "";
if (theSecondParam == NULL)
theSecondParam = "";
/* Convert the literal into a number */
firstNum = atof(theFirstParam);
secondNum = atof(theSecondParam);
/* Now multiply the numbers */
theMultiplyResult = firstNum * secondNum;
sprintf(theLiteralBuffer, "%f", theMultiplyResult);
UFLSetLiteralEx(theResult, NULL, theLiteralBuffer);
break;
/* Error if the function is unknown. */
default:
MessageBox(NULL, "The function was called with an
unrecognized function", "Error", MB_OK);
return(NOTOK);
}
break;
case FCICOMMAND_INSTANCEREGISTER:
break;
case FCICOMMAND_INSTANCEDEREGISTER:
break;
case FCICOMMAND_REGISTER:
break;
case FCICOMMAND_DEREGISTER:
break;
default:
break;
}
/* Finished */
return(OK);
}
By using the function template FunctionCallHelp, you can provide help information to form designers within a development environment (for example the Designer). Use FunctionCallHelp to help form designers choose and use the correct functions.
#ifndef OLD_STYLE_PARAMS
PRE_FUNCTION_DECL r_short POST_FUNCTION_DECL FCISimpleHelp(FunctionCall
*theObject,
r_charP thePackageName, r_charP theFunctionName, r_u_long theFunctionID,
IFSUserData
**theFunctionDataPtr, r_charP *theQuickDescPtr, r_charP *theFunctionDescPtr,
r_charP
*theSampleCodePtr, r_charP **theArgsNameListPtr, r_long *theArgsNameListSizePtr,
r_charP
**theArgsDescListPtr, r_long *theArgsDescListSizePtr, r_short
**theArgsFlagListPtr, r_long
*theArgsFlagListSizePtr, r_charP *theRetValDescPtr, r_short
*theRetValFlagPtr)
#else
PRE_FUNCTION_DECL r_short POST_FUNC_DECL FCISimpleHelp(theObject,
thePackageName,
theFunctionName, theFunctionID, theFunctionDataPtr, theQuickDescPtr,
theFunctionDescPtr,
theSampleCodePtr, theArgsNameListPtr, theArgsNameListSizePtr,
theArgsDescListPtr,
theArgsDescListSizePtr, theArgsFlagListPtr, theArgsFlagListSizePtr,
theRetValDescPtr, theRetValFlagPtr)
FunctionCall *theObject;
r_charP thePackageName;
r_charP theFunctionName;
r_u_long theFunctionID;
IFSUserData **theFunctionDataPtr;
r_charP *theQuickDescPtr;
r_charP *theFunctionDescPtr;
r_charP *theSampleCodePtr;
r_charP **theArgsNameListPtr;
r_long *theArgsNameListSizePtr;
r_charP **theArgsDescListPtr;
r_long *theArgsDescListSizePtr;
r_short **theArgsFlagListPtr;
r_long *theArgsFlagListSizePtr;
r_charP *theRetValDescPtr;
r_short *theRetValFlagPtr;
#endif
{
switch(theFunctionID)
{
case FCI_MULTIPLY_ID:
if (((*theQuickDescPtr) = cp_strdup("Multiplies two numbers
together")) == NULL)
{
MessageBox(NULL, "Out of memory", "Error", MB_OK);
return(NOTOK);
}
if (((*theFunctionDescPtr) = cp_strdup(
"This function multiplies two numbers together."
"/n It takes the values of two fields"
"/n and finds their product with a precision"
"/n of up to seven decimal places.")) == NULL)
{
MessageBox(NULL, "Out of memory", "Error", MB_OK);
return(NOTOK);
}
if (((*theSampleCodePtr) = cp_strdup(
"\t<field sid=\"PRODUCT_FIELD\">\n"
"\t\t<value compute=\"test_Package.multiply"
"(field1.value, field2.value)\"></value>\n"
"\t\t<size>\n"
"\t\t\t<ae>10</ae>\n"
"\t\t\t<ae>1</ae>\n"
"\t\t</size>\n"
"\t</field>\n")) == NULL)
{
MessageBox(NULL, "Out of memory", "Error", MB_OK);
return(NOTOK);
}
if (((*theRetValDescPtr) = cp_strdup(
"The product of the two parameters given")) == NULL)
{
MessageBox(NULL, "Out of memory", "Error", MB_OK);
return(NOTOK);
}
break;
default:
break;
}
return(OK);
}
Once you have generated the C source file for your extension, you must compile the source code to create the extension.
Once you have created and tested your extensions you can distribute them as .ifx files
To distribute your extensions so that they may be used by a specific Lotus Forms product:
To distribute your extensions so that they may be used by all Lotus Forms products:
By working through this section you have successfully built an extension. In the process you have learned how to initialize, compile and test FCI applications and use the following functions from the FCI Library:
This FCI application has been included with this API and can be found in the folder <API Program folder>\Samples\Win32\Fci\Demo\Multiply.
To view the forms provided with these sample applications, you must have a licensed or evaluation copy of the Lotus Forms Viewer installed.
Learn how to manipulate and extend forms in Microsoft Visual Basic with IBM Lotus Forms Server - COM API.
In this tutorial, you will create an application that shows you how to read, modify, and write forms using the Form Library
By working through the tutorial, you will perform all of the steps involved in creating a simple application that uses the API functions, including:
The sample application in this tutorial reads an input form called CalculateAge.xfd into memory. It retrieves the user's birth day, month, and year as well as the current date from the form. It then places these values into hidden fields in the form. This triggers the form to compute the user's age and display the result. When complete, the application saves the changes made to calculateAge.xfd as a new form called Output.xfd.
The tutorial describes the following tasks:
' Create the Main method for the program.
Sub Main()
' Declare a number of variables. TheForm represents the form, while the
' other variables are values we will read from the form.
Dim TheForm As IFormNodeP
Dim BirthYear As Integer
Dim BirthMonth As Integer
Dim BirthDay As Integer
' The program's Main consists of a number of calls to other functions.
Initialize
Set TheForm = LoadForm
BirthYear = GetBirthYear(TheForm)
BirthMonth = GetBirthMonth(TheForm)
BirthDay = GetBirthDay(TheForm)
SetBirthYear BirthYear, TheForm
SetBirthMonth BirthMonth, TheForm
SetBirthDay BirthDay, TheForm
SaveForm TheForm
' Free the memory in which the form was stored.
TheForm.Destroy
End Sub
All applications that use the API functions must initialize the Form Library to ensure correct error and memory handling behavior. The sample application does this in a separate method calledInitialize. In turn, Initialize calls the Form Library function IFSInitialize and passes it the name of the current program.
Sub Initialize()
Dim DTK As DTK
' Get the DTK object. You need this call the IFSInitialize function.
Set DTK = CreateObject("PureEdge.DTK")
' Call DTK.IFSInitialize. DTK is a static class representing the Java
' API development toolkit. The parameters are:
' 1. CalculateAge: the name of the application being run.
' 2. 1.0.0 : the version of the application being run.
' 3. 2.6.0 : the version of the API being run.
' An exception will be thrown if there is a problem.
DTK.IFSInitialize "CalculateAge", "1.0.0", "2.6.0"
End Sub
Before your program can begin working with a form, you must load it into memory. CalculateAge does this by defining a LoadForm function to handle these tasks.
Function LoadForm() As IFormNodeP
Dim XFDL As XFDL
' Get the XFDL object. You need this to call the ReadForm function.
Set XFDL = CreateObject("PureEdge.xfdl_XFDL")
' Call XFDL.ReadForm. The parameters are:
' 1. calculateAge.xfd : indicates the file on the local drive to read
' the form from.
' 2. 0 : no special behavior.
' readForm will return a reference to the root node of the
' new form structure once it is loaded into memory. An exception
' will be thrown if there is a problem.
Set LoadForm = XFDL.ReadForm("c:\calculateage.xfd", 0)
End Function
Once you have set up and initialized your application with the API and loaded a form into memory, your application is ready to start working with the form. The following code uses GetLiteralByRefEx to get a specific value from the form:
Function GetBirthDay(TheForm) As Integer
Dim BDay As String
' Call IFormNodeP.GetLiteralByRefEx to get the literal information for
' the PAGE1.BIRTHDAY.value node. An exception will be thrown if
' there is a problem.
BDay = TheForm.GetLiteralByRefEx(vbNullString, _
"PAGE1.BIRTHDAY.value", 0, vbNullString, Nothing)
' If a literal value was returned, convert it into an integer value;
' otherwise, indicate that no value was entered into the field
' and throw an exception.
If (Len(BDay) > 0) Then
GetBirthDay = CInt(BDay)
Else
MsgBox "The birth day was not entered.", vbCritical
Stop
End If
End Function
' Similar to GetBirthDay
Function GetBirthMonth(TheForm) As Integer
Dim BMonth As String
BMonth = TheForm.GetLiteralByRefEx(vbNullString, _
"PAGE1.BIRTHMONTH.value", 0, vbNullString, Nothing)
If (Len(BMonth) > 0) Then
GetBirthMonth = CInt(BMonth)
Else
MsgBox "The birth month was not entered.", vbCritical
Stop
End If
End Function
' Similar to getBirthDay()
Function GetBirthYear(TheForm) As Integer
Dim BYear As String
BYear = TheForm.GetLiteralByRefEx(vbNullString, _
"PAGE1.BIRTHYEAR.value", 0, vbNullString, Nothing)
If (Len(BYear) > 0) Then
GetBirthYear = CInt(BYear)
Else
MsgBox "The birth year was not entered.", vbCritical
Stop
End If
End Function
Once a form is loaded into memory, a developer can set the values associated with any of the item or option nodes located in the form by calling SetLiteralByRefEx.
Sub SetBirthDay(BDay As Integer, TheForm As IFormNodeP)
Dim Day As String
' Convert the birthday to a String
Day = CStr(BDay)
' Call TheForm.SetLiteralByRefEx. The parameters are:
' 1. vbNullString : reserved. Must be vbNullString.
' 2. PAGE1.HIDDENDAY.value : the reference to the node.
' 3. 0 : must be zero.
' 4. vbNullString : sets the character set to ANSI.
' 5. Nothing : no namespace node required.
' 6. Day : the value to assign to the literal.
' An Exception will be thrown if there is a problem.
TheForm.SetLiteralByRefEx vbNullString, "PAGE1.HIDDENDAY.value", _
0, vbNullString, Nothing, Day
End Sub
' Similar to SetBirthDay()
Sub SetBirthMonth(BMonth As Integer, TheForm As IFormNodeP)
Dim Month As String
' Convert the birth month to a String
Month = CStr(BMonth)
' Set the birth month as a value in the form.
TheForm.SetLiteralByRefEx vbNullString, "PAGE1.HIDDENMONTH.value", _
0, vbNullString, Nothing, Month
End Sub
' Similar to setBirthDay()
Sub SetBirthYear(BYear As Integer, TheForm As IFormNodeP)
Dim Year As String
' Convert the birth year to a String
Year = CStr(BYear)
' Set the birth year as a value in the form.
TheForm.SetLiteralByRefEx vbNullString, "PAGE1.HIDDENYEAR.value", _
0, vbNullString, Nothing, Year
End Sub
Once you have finished making the desired changes to the form, you should save it to disk. If you want to retain the original form (calculateAge.xfd), you should save the modified form under a new name. This program saves the modified form as Output.xfd.
Sub SaveForm(TheForm)
' Call theForm.writeForm. theForm is the root node of the form to
' be written. The parameters are:
' 1. output.xfd : the filename you want to use (you could also use
' a path here).
' 2. Nothing : since we do not want to set a triggeritem.
' 3. 0 : since we do not want to allow the transmit options to work.
' An exception is thrown if there is a problem.
TheForm.WriteForm "output.xfd", Nothing, 0
End Sub
Next, you must free the memory used by the form. This is the last operation in the main function of the program.
' Now that we are done with the form, we can free the memory by
' calling Destroy. The object, 'TheForm', is a reference to
' the root node of the form. This causes the root node and all
' of its children (the complete form) to be deleted from memory.
TheForm.Destroy
End Sub
If you distribute applications that use the Form Library, you will also need to distribute a number of API files. Refer to Developing and distributing applications for information about distributing applications that use the Form Library.
By working through this section you have successfully built the Calculate Age application. In the process, you have learned how to initialize, compile, and test form applications using the following methods from the Form Library:
The source code for the Calculate Age application is included with this API and can be found in the following folder:
<API Program folder>\Samples\COM\Form\Demo\Calculate_Age\
To view the forms provided with the sample application, you must have a copy of the Viewer installed.
This method performs the necessary work for your custom-built function. You will have to insert the details of your custom functions within this method.
public void evaluate( String thePackageName, String theFunctionName, int theFunctionID, int theFunctionInstance, short theCommand, com.PureEdge.xfdl.FormNodeP theForm, com.PureEdge.xfdl.FormNodeP theComputeNode, com.PureEdge.IFSUserDataHolder theFunctionData, com.PureEdge.IFSUserDataHolder theFunctionInstanceData, com.PureEdge.xfdl.FormNodeP[ ] theArgList, com.PureEdge.xfdl.FormNodeP theResult ) throws UWIException;
| Expression | Type | Description |
|---|---|---|
| thePackageName | String | The name of the package that contains the function. |
| theFunctionName | String | The name of the function. |
| theFunctionID | int | A unique number that can be used to identify the function. |
| theFunctionInstance | int | A unique number that differentiates one instance of the function from another instance. See Notes for more information. |
| theCommand | short | The name of the command for this method to perform. See Notes for more information. Other commands can be found within the manual. |
| theForm | FormNodeP | The form that contains the function. |
| theComputeNode | FormNodeP | The node within the form that stores the function. See Notes for more information. |
| theFunctionData | IFSUserDataHolder | Reserved. Although this expression is not used, it must be present. |
| theFunctionInstanceData | IFSUserDataHolder | Reserved. Although this expression is not used, it must be present. |
| theArgList | FormNodeP [ ] | The list of arguments. See Notes for more information. |
| theResult | FormNodeP | The FormNodeP object in which you should store the result. Simply use setLiteralEx on this object to store the result. |
Nothing if call is successful or throws a generic exception (UWIException) if an error occurs.
<LABEL SID = "L1">
<VALUE COMPUTE = "testPackage.multiply('7', '6')"></VALUE>
</LABEL>
Then theComputeNode will point to the node that represents the value option.
theArgList[0].getLiteralEx(null)
public class FciFunctionCall extends com.PureEdge.xfdl.FunctionCallImplBase
implements FunctionCall
{
public static final int FUNCTION_ID = 1;
/* Additional Code Removed */
public void evaluate(String thePackageName,
String theFunctionName, int theFunctionID,
int theFunctionInstance, short theCommand,
com.PureEdge.xfdl.FormNodeP theForm,
com.PureEdge.xfdl.FormNodeP theComputeNode,
com.PureEdge.IFSUserDataHolder theFunctionData,
com.PureEdge.IFSUserDataHolder theFunctionInstanceData,
com.PureEdge.xfdl.FormNodeP [] theArgList,
com.PureEdge.xfdl.FormNodeP theResult) throws UWIException
{
/* The first switch in this method is based on theCommand. The only case
that we are interested in handling is FCICOMMAND_RUN that indicates
that we should evaluate a function. */
switch (theCommand)
{
case FunctionCall.FCICOMMAND_RUN:
/* The second switch is based on theFunctionID that you set for each
of your custom functions. This makes it easy for a single FunctionCall
object to support multiple functions. */
switch(theFunctionID)
{
case FciFunctionCall.FUNCTION_ID:
/* Insert the Details of your custom function here */
break;
}
break;
default:
break;
}
}
/* Additional code Removed */
}
Provides help information about each of your custom functions in the form development environment (for example, Lotus Forms Designer).
public void help( String thePackageName, String theFunctionName, int theFunctionID, com.PureEdge.IFSUserDataHolder theFunctionData, com.PureEdge.StringHolder theQuickDesc, com.PureEdge.StringHolder theFunctionDesc, com.PureEdge.StringHolder theSampleCode, com.PureEdge.StringListHolder theArgsNameList, com.PureEdge.StringListHolder theArgsDescList, com.PureEdge.ShortListHolder theArgsFlagList, com.PureEdge.StringHolder theRetValDesc, com.PureEdge.ShortHolder theRetValFlag ) throws UWIException;
| Expression | Type | Description |
|---|---|---|
| thePackageName | String | The name of the package that contains the function. |
| theFunctionName | String | The name of the function. |
| theFunctionID | int | A unique number that can be used to identify the function. |
| theFunctionData | IFSUserDataHolder | Reserved. Although this expression is not used, it must be present. |
| theQuickDesc | StringHolder | The method sets a short one-line description of what the function does. |
| theFunctionDesc | StringHolder | The method will set a longer more detailed description of the function. |
| theSampleCode | StringHolder | The method will set an example of the XFDL code used to call your function, including an example of the function parameters. |
| theArgsNameList | StringListHolder | The method will set a list of arguments that your function takes. See Notes for more information. |
| theArgsDescList | StringListHolder | The method will set a description of each of the arguments in the theArgsNameList. See Notes for more information.. |
| theArgsFlagList | ShortListHolder | The method will set a list of bit flags representing the type of each argument that the function takes. See Notes for more information. |
| theRetValDesc | StringHolder | The method will set a description of your custom function's return value. |
| theRetValFlag | ShortHolder | The method will set a bit flag representing the type of the return value. See Notes for more information. Simply use setLiteralEx on this object to store the result. |
Nothing if call is successful or throws a generic exception (UWIException) if an error occurs.
theArgsNameList.value = null;
public void help(String thePackageName,
String theFunctionName, int theFunctionID,
com.PureEdge.IFSUserDataHolder theFunctionData,
com.PureEdge.StringHolder theQuickDesc,
com.PureEdge.StringHolder theFunctionDesc,
com.PureEdge.StringHolder theSampleCode,
com.PureEdge.StringListHolder theArgsNameList,
com.PureEdge.StringListHolder theArgsDescList,
com.PureEdge.ShortListHolder theArgsFlagList,
com.PureEdge.StringHolder theRetValDesc,
com.PureEdge.ShortHolder theRetValFlag) throws UWIException
{
/* Switch on theFunctionID. This makes it easy for a single FunctionCall
object to support multiple functions. */
switch(theFunctionID)
{
/* Remember that you must define an ID number for each custom function
that you create. In the example below the constant MULTIPLY represents
the identification number for the multiply function. */
case FciFunctionCall.MULTIPLY:
theQuickDesc.value = "multiplies two numbers together";
theFunctionDesc.value = "This function takes two numeric " +
"parameters and multiplies the two numbers together and " +
"returns the result.";
theSampleCode.value = "\tlabel1 = new label\n" + "\t{\n" +
"\t\tvalue = testPackage.multiply(\"10\", field2.value);\n" +
"\t\tsize = [\"10\", \"1\"];\n" + "\t}\n" +
"\tfield2 = new field\n" +
"\t{\n" + "\t\tvalue = \"7\";\n" + "\t}\n";
/* Notice that in defining theArgsNameList below, you must create
the list before providing a value for each element in the list. */
theArgsNameList.value = new String[2];
theArgsNameList.value[0] = "number1";
theArgsNameList.value[1] = "number2";
/* Notice that in defining theArgsDescList below, you must create
the list before providing a value for each element in the list. */
theArgsDescList.value = new String[2];
theArgsDescList.value[0] = "The first number";
theArgsDescList.value[1] = "The second number";
theRetValDesc.value = "The result";
break;
}
}
public void registerFunctionCall( com.PureEdge.xfdl.FunctionCall theFCIInterface, String thePackageName, String theFunctionName, int theFunctionID, int theFlags, String theCallingParams, int theVersion, String theQuickDesc ) throws UWIException;
| Expression | Type | Description |
|---|---|---|
| theFCIInterface | FunctionCall | The FunctionCall object that will handle requests for the function. Setting: The FunctionCall object that is registering the function. See Notes for more information. |
| thePackageName | String | The name of the package that will contain the function. |
| theFunctionName | String | The name of the function. |
| theFunctionID | int | A unique number that can be used to identify the function. See Notes for more information. |
| theFlags | int | A set of flags which indicate how the custom function will be evaluated. Setting: Typically FCI_FOLLOWS_STRICT_CALLING_PARAMETERS or 0. See Notes for more information. |
| theCallingParams | String | The list of parameters that this function takes. Setting: S, O, or R. See Notes for more information. |
| theVersion | int | The version number of the function. Setting: Function Version Number. See Defining a Version Number for more information. |
| theQuickDesc | String | A short, one-line description of what the function does. |
Nothing if call is successful or throws a generic exception (UWIException) if an error occurs.
public static final int MULTIPLY_ID = 1;
"S,SO"
public class FciFunctionCall extends com.PureEdge.xfdl.FunctionCallImplBase
implements FunctionCall
{
public static final int FUNCTION_ID = 1;
/* Additional Code Removed */
theFCM.registerFunctionCall(this,"sample_package",
"multiply",FciFunctionCall.FUNCTION_ID,
FCI.FCI_FOLLOWS_STRICT_CALLING_PARAMETERS, "S,S", 0x01000300,
"Multiplies two numbers");
}
public void registerInterface( com.PureEdge.GenericInterface theInterface, String theInterfaceName, int theInterfaceVersion, int theMinInterfaceVersion, int theImplementationVersion, int theFlags, String [] theCriteriaList, com.PureEdge.ifx.IFXCriteriaMatchingHandler theCriteriaHandler ) throws UWIException;
| Expression | Type | Description |
|---|---|---|
| theInterface | GenericInterface | The object that you are registering with the IFX Manager. Typical setting: this (if the object is registering itself) |
| theInterfaceName | String | The name of the Interface that you are registering. In this case a Function Call Interface. Typical setting: FunctionCall.FUNCTIONCALL_INTERFACE_ NAME |
| theInterfaceVersion | int | The function call interface version. Typical setting: FunctionCall.FUNCTIONCALL_CURRENT_ VERSION |
| theMinInterface Version | int | The minimum version that the interface will support. Typical setting: FunctionCall.FUNCTIONCALL_ MIN_VERSION_SUPPORTED |
| theImplementation Version | int | The version of the object you are registering. This is typically 0x01000300. If there are multiple objects with the same name available, the one with the highest version number is registered. |
| theFlags | int | Reserved. Setting: 0 |
| theCriteriaList | String [ ] | Reserved. Setting: null |
| theCriteriaHandler | com.PureEdge.ifx.IFX Criteria-Matching Handler | Reserved. Setting: theFCM.getDefaultListener( ) |
Nothing if call is successful or throws a generic exception (UWIException) if an error occurs.
theFCM.getDefaultListener( )
Note that theFCM is a FunctionCallManager object which represents the Function Call Manager.
In the following example, theIFX represents the IFX Manager
public FciFunctionCall(IFX IFXMan) throws UWIException
{
FunctionCallManager theFCM;
if ((theFCM = IFSSingleton.getFunctionCallManager()) == null)
throw new UWIException("Needed Function Call Manager");
IFXMan.registerInterface(this,
FunctionCall.FUNCTIONCALL_INTERFACE_NAME,
FunctionCall.FUNCTIONCALL_CURRENT_VERSION,
FunctionCall.FUNCTIONCALL_MIN_VERSION_SUPPORTED,
0x01000300, 0, null, theFCM.getDefaultListener( ));
}
The following table lists the constants that are used within the FunctionCall class along with a short description of each constant:
This section provides an overview of an XFDL form as it is represented in memory. You must understand the memory structure of a form to effectively develop applications using the API.
When a form is loaded into memory, it is constructed as a series of linked nodes. Each node represents an element of the form, and together these nodes create a tree that describes the form. The following diagram illustrates the general composition of a single node.

Each node within the tree has the following properties:
Depending on the node type, some of these properties may be null.
Every node is part of an overall hierarchy that describes the complete form. This hierarchy follows a standard tree structure, with the top of the tree being the top (or root) of the hierarchy.
The diagram in Figure 1 shows the tree structure for a simple form.
The elements of the hierarchy, in descending order, are:
A sample node hierarchy diagram is shown below:
References allow you to identify a specific page, item, option, or argument by providing a "path" to that element. This means that you can access an element directly without having to locate any of its ancestors. The syntax of a reference follows this general pattern:
page.item.option[argument]
Each element of the reference is constructed as follows:
Arguments can also have any depth. For example, you might have an argument that contains arguments. You can reference additional levels of depth by adding another bracketed reference. For example, to refer to the first argument in the first argument of the printsettings option, you could use either [0][0] or the tag names in brackets, such as [pages][filter].
You can create references to any level of the node hierarchy. For example, the following table illustrates a number of references starting at different levels of the form:
| Start At | Ref to Page | Ref to Item | Ref to Option | Ref to Argument |
|---|---|---|---|---|
| Page | Page1 | Page1.Field1 | Page1.Field1.format | Page1.Field1.format[message] |
| Item | — | Field1 | Field1.format | Field1.format[message] |
| Option | — | — | format | format[message] |
| Argument | — | — | — | [message] |
When making a reference to an item node, there may be times when you do not know which node to reference because it depends on some action from the user of the form. Consider a situation in which a user selects a cell from a list. Because you don't know beforehand which cell the user will choose, it is not possible to explicitly reference the item node for the chosen cell. In such cases you would use dereferencing to retrieve the node indirectly.
Essentially, dereferencing allows you to make a dynamic reference that is evaluated at runtime. This is accomplished by placing the -> symbol to the right of the dynamic reference.
For example, consider a list item called List1 that has three cells called Cell1, Cell2, Cell3. If you want to access the item node of the cell selected by the user, use the following reference string:
List1.value->
At runtime, the portion of the expression that is to the left of the dereference symbol is evaluated and replaced. If the user chooses the second cell, List1.value is evaluated and replaced with:
Cell2
As a result, the item node for Cell2 is returned.
In some cases, instead of accessing the item node of the chosen cell, you may want to access one of the cell's option nodes. Again, dereferencing is used. The reference string would be:
List1.value->value
As before, the above expression is evaluated at runtime. The expression to the left of the dereference symbol is evaluated and replaced, just as before. So if the second cell is selected, List1.value is evaluated as Cell2. This value is then concatenated with the expression to the right of the dereference symbol. This produces:
Cell2.value
As a result, the option node for Cell2.value is returned.
References that include options or arguments in any namespace other than XFDL normally require the inclusion of the namespace prefix in the reference. For example, if you want to reference "myOption" in the "custom" namespace, refer to that option as "custom:myOption" as shown:
page_1.myItem.custom:myOption
If you are referencing named arguments, you should also use the appropriate namespace. For example:
page_1.myItem.custom:myOption[custom:myArgument]
However, if you are referencing an argument by index number you do not need to worry about namespace. All arguments, regardless of namespace, are indexed in order. For example, if "myOption" contains two arguments, the first in the XFDL namespace and the second in the custom namespace, use the following reference for the second argument:
page_1.myItem.custom:myOption[1]
In some cases, forms may have no default namespace or may have a default namespace that is explicitly set to an empty string. In these cases, you can use null as the prefix for the empty namespace. For example, the following field declares a default namespace that is empty:
<page sid="Page1">
<field sid="myField" xmlns="">
<value>Test Value</value>
</field>
</page>
In this case, to reference the value of the field, you would use the null prefix as shown:
Page1.null:myField.null:value
When an XFDL form is stored in memory, it exists as a series of nodes that are linked in a tree structure. As described in The Node Hierarchy, the tree structure follows this hierarchy: form, page, item, option, and argument.
Within a single branch of the tree, all elements of the same level are treated as siblings, each of which has a common parent, and each of which may have its own children.
The following example illustrates the node structure of a simple form, and gives a top-down description of the node structure.
The following XFDL code creates the node hierarchy shown in Figure 1. The result is a simple form that contains three items (a line and two labels).
<?xml version = "1.0"?>
<XFDL xmlns="http://www.ibm.com/xmlns/prod/XFDL/7.5"
xmlns:xfdl="http://www.ibm.com/xmlns/prod/XFDL/7.5">
<globalpage sid="global">
<global sid="global"></global>
</globalpage>
<page sid = "PAGE1">
<global sid="global"></global>
<line sid = "REFLINE">
<size>
<width>20</width>
<height>0</height>
</size>
</line>
<label sid = "LABEL1">
<value>Hello</value>
</label>
<label sid = "LABEL2">
<value>World</value>
<itemlocation>
<after>LABEL1</ae>
<expandr2r>REFLINE</expandr2r>
</itemlocation>
</label>
</page>
</XFDL>
Each tree begins with the form, or root, node. This node contains no information - it simply represents the starting point of the tree structure.
Below the form node are the page nodes. In the previous example, there are two page nodes: "global" and "PAGE1". The "global" page node stores any global settings that apply to the form, and "PAGE1" stores the contents of the first form page. Additional pages are stored as siblings of the "PAGE1" node.
Below each page node are the item nodes. As shown in the previous example, the first item node of a page is always the "global" item. The "global" item stores settings that apply to the items on that page. Each additional item in the page is stored as a sibling of the global item.
Below each item node are the option nodes. Each option node represents an option setting for that item, such as a background color or font setting.
Below each option node are the argument nodes. These nodes contain the settings for the parent option. For example, the background color might be set to "blue". There can be an infinite number and depth of these nodes, depending upon the number and depth of the settings for that option.
For instance, in the sample form, the size node for "REFLINE" has two argument nodes: one for the width and one for the height. In contrast, the printsettings option can have multiple argument nodes which themselves have argument nodes as children. The following is an example of the node structure of the printsettings option:


Thus, in storing the printsettings option, two levels of argument nodes are created. The first level describes the number of array elements in the option (two). The second level gives the arguments for each element.
Due to their potential complexity, pay careful attention to the mapping of argument nodes.
There are several levels of nodes in an XFDL form: form (or root), page, item, option, and argument (which can have an infinite number of levels). Each node has four properties: literal, type, identifier, and compute. A node does not necessarily contain information for every property.
For example, a page node can never have values for the compute or literal properties. And while a value for the user data property is optional, a page node must always have values for the type and identifier properties.
The following table illustrates what properties may be in use for each node level.

yes — node can have that property
always — node always has that property
no — node cannot have that property
The API contains two libraries; the Form library and the FCI library. You use the Form library for reading, parsing, modifying, and writing forms. The FCI library allows you to create custom functions that extend the capabilities of forms.
You use the form library to develop applications that manipulate XFDL forms. Your C and Java applications use the form library to:
The Function Call Interface (FCI) API provides a means for creating extremely powerful form applications in a simple and elegant manner.
The FCI Library is a collection of methods for developing custom-built functions that form developers may call from XFDL forms. By creating custom functions, you can extend the capabilities of forms without requiring an upgrade to either your forms software or the form description language (XFDL). Using the methods from this library you can:
The purpose of the FCI is to make the functionality of forms extensible without requiring updates to your forms driver software. This API allows you to create self-contained modules called extensions that provide packages of functions for use in XFDL forms.
Functions can be used almost anywhere in an XFDL form; the appropriateness of their use depends mainly on their behavior. For instance the XFDL Specification contains a default package of functions called system. Every application built with the API version 4.4 or greater can use these functions.
Functions are grouped together to form packages. When you call a function from a form, you must include the function's package name in the call. For example, the function beep is part of the package called my_funcs. To call the beep function from a form and assign the result to the form option do_beep you would type the following:
<label sid = "do_beep">
<value compute = "my_funcs.beep( )"></value>
</label>
The most common use of a function is to return a value that is used to set a form option, such as the value of a field. For example, the toupper function in the system package, which converts a string to upper case and returns the result, might be used to set the value of a particular form field. This method could take as its sole argument the value of a label elsewhere on the form (or on another form) and convert it to upper case as follows:
<label sid = "SomeLabel">
<value>"I am a label"</value>
</label>
<field sid = "SomeField">
<size>
<ae>20</ae>
<ae>1</ae>
</size>
<value compute = "system.toupper(SomeLabel.value)"></value>
</field>

Refer to the The FCI Extension Architecture for more information. .
Use the following rules to help you define your own packages and extensions:
Once you have created your extensions you can embed them directly into XFDL forms, or you can distribute them to users as Java Archive files (JARs) or as ZIP files.
The main purpose of package names is to distinguish the functions in a package from those in other packages that could potentially have the same names. All packages you create must contain an underscore in their names. For example, the convertDate function belongs to a package called sample_package.
The FCI is itself an extension. It is currently only available for Windows 32-bit applications. A set of Java wrapper classes, supplied as a Java Archive file (JAR file) or ZIP file, provide a Java interface to the DLL.
The Form Library provides developers with tools for accessing and manipulating XFDL forms as structured data types. For instance, methods in the Form Library will provide your applications with a means for reading and writing forms, retrieving information contained in form elements or assigning information to the elements of a form.
The FCI Library of methods allows you to create an extension structure that contains one or more packages of your custom functions.
Once you have set up the framework for your custom functions you can use Java system methods, Form Library methods or even other FCI methods to implement the details of each function.

Extensions can exist in any of the following locations:
When the Forms System is initialized, the API checks for extensions. If it finds any, it calls the initialization method for each extension and passes each method an object called the IFX Manager.

As part of the initialization, those extensions that provide a function call interface create one or more FunctionCall objects.

Then, each FunctionCall object requests a FunctionCallManager object from the IFX Manager.

Each FunctionCall object registers itself with the IFX Manager as a function call and then registers your custom-built functions and corresponding packages with the Function Call Manager.


The final result is an extension containing a registered FunctionCall object. The registered FunctionCall object contains your package of custom functions.

When a function is called in a form, the forms driver requests the package and function from the API. The API will use the Function Call Manager to locate the FunctionCall object that contains the requested function and evaluate it.

Although this procedure is specific to Microsoft Visual C++ 6.0, you can use it as a guideline to set up alternate development environments.
To set up your development environment to use the Form Library, follow this procedure:
Although this procedure is specific to Microsoft Visual C++ 6.0, you can use it as a guideline to set up alternate development environments.
To set up your development environment to use the FCI Library, follow this procedure:
<WIN SYS>\PureEdge\80\java\classes\StreamingAPI.jar
To set up your computer to run applications that use the API:
/usr/lib/ /usr/lib/PureEdge/80/system/ <path_to_directory_containing_the_Netscape_Security_Libraries>
/lib /usr/lib /usr/lib/PureEdge/80/system <path_to_JVM>/jre/bin/classic <path_to_JVM>/jre/bin <path_to_directory containing the Netscape Security Libraries>
You must configure your integrated development environment to use the API. You must also ensure that your computer can run applications that use the API.
/usr/lib/PureEdge/80/java/classes/pe_api.jar /usr/lib/PureEdge/80/java/classes/uwi_api.jar /usr/lib/PureEdge/80/java/classes/commons-codec.jar /usr/lib/PureEdge/80/java/classes/xmlsec-1.4.1.jar
You must configure your integrated development environment to use the API. You must also ensure that your computer can run applications that use the API.
/usr/lib/PureEdge/80/java/classes/StreamingAPI.jar
If you plan to distribute applications that call the API, you must ensure the following:
The PureEdgeAPI.ini file determines which applications use which version of the API. This ensures that older applications do not attempt to use newer APIs, which may not be compatible.
The PureEdgeAPI.ini file must be placed in the Windows folder. If no PureEdgeAPI.ini file exists on the target computer, you should install your own. If a PureEdgeAPI.ini file already exists, you should update the file to include an entry for your application. For more information about setting up the PureEdgeAPI.ini file for your application, refer to the IFSInitialize function if you are using C, or theinitialize method if you are using Java.
The prefs.config file controls the configuration of the API. When you distribute an application that uses the API, you must also distribute a configuration file for that application. This file ensures that the API is configured properly to work with your application.
You can install the configuration file in any of the following locations:
c:\Documents and Settings\<username>\Application Data\PureEdge\
<application> <version>\Prefs\prefs.config
c:\Documents and Settings\All Users\Application Data\PureEdge\
<application> <version>\Prefs\prefs.config
c:\Documents and Settings\<username>\Application Data\PureEdge\
API <version>\Prefs\prefs.config
c:\Documents and Settings\All Users\Application Data\PureEdge\
API <version>\Prefs\prefs.config
These locations are listed in order of precedence, from highest to lowest. When loading the configuration information, the API first checks the lowest precedence location, then works its way to the highest. The configuration settings are loaded from each location, but settings in a higher precedence location override those in a lower precedence location.
In these paths, the <application> and <version> are determined by the parameters of the IFSInitialize function. You must call this function from your application to initialize the API, and as part of that you must supply a name and version for your application. Use the same name and version in your path.
For example, if you called the IFSInitialize function with a name of "CustomApp" and a version of "1.0", you would install the configuration file in the following location for All Users:
c:\Documents and Settings\All Users\Application Data\PureEdge\
CustomApp 1.0\Prefs\prefs.config
For more information about the IFSInitialize function, refer to the Lotus Forms Server API – C API User's Manual.
Before a computer can run applications that use the Java API or use Java extension files, you must set your computer's CLASSPATH environment variable to include the following files:
<WIN SYS>\PureEdge\80\java\classes\pe_api.jar <WIN SYS>\PureEdge\80\java\classes\pe_api_native.jar <WIN SYS>\PureEdge\80\java\classes\uwi_api.jar <WIN SYS>\PureEdge\80\java\classes\uwi_api_native.jar <WIN SYS>\PureEdge\80\java\classes\commons-codec.jar <WIN SYS>\PureEdge\80\java\classes\xmlsec-1.4.1.jar
If you plan to distribute applications that call the API, you must ensure the following:
Identifies the directories that need to be redistributed.
You must have a PureEdgeAPI.ini file to successfully create Lotus Forms applications
The PureEdgeAPI.ini file is located in the following directory:
/etc
If no PureEdgeAPI.ini file exists, you should install your own. If a PureEdgeAPI.ini file already exists, you should update the file to include an entry for your application. For more information about setting up the PureEdgeAPI.ini file for your application, refer to the IFSInitialize function if you are using C, or theinitialize method if you are using Java.
The prefs.config file controls the configuration of the API. When you redistribute the API with your application, you must also install an application-specific configuration file on the target computers. You should set this file to configure the API properly for your application.
/home/<username>/.PureEdge/<application>_<version>/
prefs/prefs.config
/etc/PureEdge/prefs/prefs.config
For example, if you called the IFSInitialize function with a name of "CustomApp" and a version of "1.0", you would install the configuration file in the following location:
/home/<username>/.PureEdge/CustomApp_1.0/prefs/prefs.config
Before a computer can run applications that use the API, you must set its CLASSPATH environment variable to include the following files:
/usr/lib/PureEdge/80/java/classes/pe_api.jar /usr/lib/PureEdge/80/java/classes/pe_api_native.jar /usr/lib/PureEdge/80/java/classes/uwi_api.jar /usr/lib/PureEdge/80/java/classes/uwi_api_native.jar /usr/lib/PureEdge/80/java/classes/commons-codec.jar /usr/lib/PureEdge/80/java/classes/xmlsec-1.4.1.jar
Additionally, on a Linux or Solaris system, include the following directory in the LD_LIBRARY_PATH environment variable:
/usr/lib/:/usr/lib/PureEdge/80/system/
On an AIX system, include the following directories in the LIBPATH environment variable:
/lib /usr/lib /usr/lib/PureEdge/80/system /<path to JVM>/jre/bin/classic /<path to JVM>/jre/bin
This information was developed for products and services offered in the U.S.A.
IBM may not offer the products, services, or features discussed in this document in other countries. Consult your local IBM representative for information on the products and services currently available in your area. Any reference to an IBM product, program, or service is not intended to state or imply that only that IBM product, program, or service may be used. Any functionally equivalent product, program, or service that does not infringe any IBM intellectual property right may be used instead. However, it is the user's responsibility to evaluate and verify the operation of any non-IBM product, program, or service.
IBM may have patents or pending patent applications covering subject matter described in this document. The furnishing of this document does not grant you any license to these patents. You can send license inquiries, in writing, to:
IBM Director of Licensing
IBM Corporation
North Castle Drive
Armonk, NY 10504-1785
U.S.A.
For license inquiries regarding double-byte (DBCS) information, contact the IBM Intellectual Property Department in your country or send inquiries, in writing, to:
Intellectual Property Licensing
Legal and Intellectual Property Law
IBM Japan Ltd.
1623-14, Shimotsuruma, Yamato-shi
Kanagawa 242-8502 Japan
The following paragraph does not apply to the United Kingdom or any other country where such provisions are inconsistent with local law: INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer of express or implied warranties in certain transactions, therefore, this statement may not apply to you.
This information could include technical inaccuracies or typographical errors. Changes are periodically made to the information herein; these changes will be incorporated in new editions of the publication. IBM may make improvements and/or changes in the product(s) and/or the program(s) described in this publication at any time without notice.
Any references in this information to non-IBM Web sites are provided for convenience only and do not in any manner serve as an endorsement of those Web sites. The materials at those Web sites are not part of the materials for this IBM product and use of those Web sites is at your own risk.
IBM may use or distribute any of the information you supply in any way it believes appropriate without incurring any obligation to you.
Licensees of this program who wish to have information about it for the purpose of enabling: (i) the exchange of information between independently created programs and other programs (including this one) and (ii) the mutual use of the information which has been exchanged, should contact:
IBM Corporation
5 Technology Park Drive
Westford Technology Park
Westford, MA 01886
U.S.A.
Such information may be available, subject to appropriate terms and conditions, including in some cases, payment of a fee.
The licensed program described in this document and all licensed material available for it are provided by IBM under terms of the IBM Customer Agreement, IBM International Program License Agreement or any equivalent agreement between us.
IBM, the IBM logo, ibm.com, Lotus, and Notes are trademarks or registered trademarks of International Business Machines Corporation in the United States, other countries, or both. These and other IBM trademarked terms are marked on their first occurrence in this information with the appropriate symbol (® or ™), indicating US registered or common law trademarks owned by IBM at the time this information was published. Such trademarks may also be registered or common law trademarks in other countries. A current list of IBM trademarks is available on the Web at http://www.ibm.com/legal/copytrade.shtml
Java and all Java-based trademarks and logos are trademarks or registered trademarks of Oracle and/or its affiliates.
Linux is a registered trademark of Linus Torvalds in the United States, other countries, or both.
Microsoft and Windows are trademarks of Microsoft Corporation in the United States, other countries, or both.
UNIX is a registered trademark of The Open Group in the United States and other countries.
Other company, product, or service names may be trademarks or service marks of others.