Business Object Wizard calls the getTreeNodes() method to discover the source nodes in the ODA's data source and create the source-node hierarchy, which Business Object Wizard displays in its Select Source dialog box (Step 3). The getTreeNodes() method is part of the IGeneratesBoDefs interface, which the ODA class must implement to support generation of business object definitions.
As Selecting and confirming source data describes, Business Object Wizard uses the tree-node array that getTreeNodes() returns to initialize the Select Source dialog box. This dialog box displays the source-node hierarchy, which allows users to move through the source nodes obtained from the data source and to select those for which the ODA generates business object definitions. Each time a source node is expanded, Business Object Wizard calls the getTreeNodes() method, which returns a tree-node array with the contents of the expanded source node.
For example, the getTreeNodes() method in the sample Roman Army ODA initializes the Select Source dialog box with the top-level army general, which it has obtained from the sample's data source, the RomanArmy.xml file. When a particular node is expanded, getTreeNodes() obtains the sons of that node's army general from the XML file and puts them into a tree-node array. Business Object Wizard uses this tree-node array to display the expanded source node.
Therefore, the purpose of getTreeNodes() is to discover source nodes in the data source, then construct and return an array of tree nodes. To do this, getTreeNodes() performs the following tasks:
When Business Object Wizard calls the getTreeNodes() method, it passes to this method the value of the parent-node path. This path identifies the user-selected node that getTreeNodes() will expand. It is a String that contains the fully qualified path of the node, from the top-level parent down to the user-selected node. Node names within this path are separated with a colon (:).
For example, Figure 59 shows a Select Source dialog box that displays a view of the source-node hierarchy for the sample Roman Army ODA.
Figure 59.
Sample source-node hierarchy
In Figure 59, the parent-node path for the Uulius source node is:
Apollo:Tellus:Uulius
Users can specify a parent node to expand by one of the following ways:
Business Object Wizard constructs the parent-node path for the selected source node and passes this path to getTreeNodes().
You must specify the parent-node path with the same syntax that getTreeNodes() expects for its parent-node path. For more information, see Specifying an object path.
The getTreeNode() method uses the parent-node path to determine the level of the source-node hierarchy to return in its tree-node array. This tree-node array will contain all child nodes of the node that the parent-node path identifies. To tell getTreeNodes() to return the top-level of the source-node hierarchy, Business Object Wizard passes in an "empty" parent node path. Therefore, the getTreeNodes() method should check for an empty node path as its first step, as the following code fragment shows:
if (parentNodePath = null || parentNodePath.length() == 0) //return the top-level of the source-node hierarchy
If the parent-node path is not empty, getTreeNodes() should build the tree nodes for the children of the specified parent-node path, returning the appropriate array of TreeNode objects to Business Object Wizard.
Figure 60 shows the implementation of the getTreeNodes() method (defined in the ArmyAgent3 class of the sample Roman Army ODA).
Figure 60. Generating the tree-node array
public TreeNode[] getTreeNodes(String parentNodePath, String searchPattern) throws ODKException { if (parentNodePath == null || parentNodePath.length() == 0) return getNodes(m_army, searchPattern); return getNodes(findSon(parentNodePath, searchPattern)); }
Figure 60 shows an important concept in the implementation of the getTreeNodes() method. This method is often modularized, putting the actual search of the data source into a separate method or even into a separate class. This getTreeNodes() method calls the getNodes() method to actually generate the tree-node array for the selected data-source data. If the parent-node path is empty, getTreeNodes() sends to getNodes() the entire contents of the XML file (in the m_army variable). Otherwise, getTreeNodes() sends to getNodes() the results of the findSon() method, which performs the actual query of the data source.
A search pattern allows you to specify criteria that child nodes must meet to be displayed when the parent node is expanded. You initiate the search-pattern feature by right-clicking and then clicking Search for items. The Enter a Search Pattern dialog box opens, where you can specify the search criteria.
When Business Object Wizard receives a search pattern, it calls getTreeNode() again to retrieve a new tree-node array from the data source. Business Object Wizard passes the search pattern as an argument to getTreeNodes(). The search pattern contains wildcards and other symbols that the underlying data source recognizes. For example, if the data source is a database, valid search criteria could include SQL search symbols such as a percent (%) or question mark (?).
The getTreeNodes() method searches the data source for child nodes that match the search pattern and puts the resulting child nodes in the tree-node array that it returns to Business Object Wizard. In this way, you can dynamically specify new conditions for source nodes to meet.
You should also initialize the searchPatternDesc member variable to a string that describes to the user the valid search criteria. Business Object Wizard displays this string as the text for the Enter the Search Pattern dialog box. You initialize the ODA's metadata in the getMetaData() method. For more information, see Initializing ODA metadata.
For example, if the data source is a database, you can include the search pattern in SQL statements that query the database tables.
In the sample Roman Army ODA, the search pattern allows the user to enter one letter as search criteria. The getTreeNodes() method calls the getNodes() method to handle the actual generation of tree nodes. The following code fragment from this getNodes() method (defined in ArmyAgent3) shows how the method uses the search pattern in its search of the data source:
TreeNode[] getNodes(Son parent, String searchPattern) { Vector nodes = new Vector();
if (searchPattern == null || searchPattern.length() == 0) searchPattern = ""; else searchPattern = new String(new char[] {searchPattern.charAt(0)});
When the getNodes() method later compares the name of the object in the XML file (the data source) with the current name in the parent-node path, it checks if the object name begins with the specified search pattern, as follows:
if (currSon.name.getValue().startsWith(searchPattern))
For the context of this use of the search pattern, see Figure 61.
The main purpose of the getTreeNodes() method is to query the data source to discover source nodes, which are objects for which the ODA can generate content. The mechanism to query the data source depends on the kind of data source with which the ODA works. For example, the XML ODA (a prebuilt ODA that is part of the WebSphere Business Integration Adapters product) queries XML files to present the names of objects within these files for possible content generation. As another example, the JDBC ODA (another prebuilt ODA part of WebSphere Business Integration Adapters) queries a JDBC database to present the names of tables within the database for possible content generation.
As suggested in Table 25, if the logic necessary to query your data source is reasonably complex, you should develop special Java classes to handle this interaction. The getTreeNodes() method can then instantiate and access these classes as needed. Make sure you include these classes in the ODA library file. For more information, see Compiling the ODA.
For the sample Roman Army ODA, the findSon() method (defined in the ArmyAgent3 class) performs the task of querying the data source. It finds a particular soldier (identified by its parent-node path) in the Roman-Army XML file. It returns the information for the specified name as a Son object. The sample defines the Son class to read an object in the XML file.
As the ODA queries the data source for source nodes, it must generate the associated tree node to represent each source node it discovers. The ODK API represents a tree node as an object of the TreeNode class, which contains the information shown in Table 39.
Contents of a tree nodeTo create a tree node, use one of the forms of the TreeNode() constructor. For a list of these forms, see TreeNode().
When a node has a "normal" nature, it can have one of the following structures:
Business Object Wizard displays an expandable node with a plus
(+) sign to the left of the node name (to indicate that the user
can expand the node) or a minus
(-) sign (to indicate that the user can contract the node). The
following table shows the initialization that the TreeNode
object requires for it to display as an expandable node:
TreeNode member variable | Value |
---|---|
isExpandable |
true |
nodes |
An array of the child nodes |
isGeneratable |
false (usually) |
For information about how to move through the source-node hierarchy, see Moving through the source-node hierarchy
Business Object Wizard displays a leaf (terminating) node as just the node name. The following table shows the initialization that the TreeNode object requires for it to display as a leaf node:
Tree-node member variable | Value |
---|---|
isExpandable |
false |
nodes |
null |
isGeneratable |
true (usually) |
Both the leaf and expandable node are normal-nature nodes. Therefore, they both have the polymorphicNature member variable set to the NODE_NATURE_NORMAL node-nature constant. This constant is defined in the ODKConstant interface (which the TreeNode class implements). The first two forms of the TreeNode() constructor do not specify the polymorphicNature member variable. Therefore, this member variable defaults to NODE_NATURE_NORMAL.
Suppose the ODA generates the source-node hierarchy shown in Figure 59. If the user expands the Uulius node, the getTreeNodes() method must generate a tree-node array that contains the child nodes for Uulius. Because the parent-node path is not empty (it is Apollo:Tellus:Uulius), the getTreeNodes() method makes the following call to the getNodes() method (see Figure 60):
getNodes(findSon(parentNodePath), searchPattern))
This call to getNodes() uses the findSon() method to query the data source for the Uulius node and return a Son object that contains the information from the XML file. One of the member variables in this Son object is a vector of XML objects (XmlObjectVector) with the information on the children of Uulius. Figure 61 shows a code fragment from the getNodes() method that loops through this XML-object vector and creates a TreeNode object for each of the children:
Figure 61. Constructing tree nodes
for (int i=0; i<sons.size(); i++) { Son currSon = (Son) sons.getAt(i); if (currSon.name.getValues().startsWith(searchPattern)) { int age = currSon.age.getIntValue(); int children = currSon.Son == null ? 0 : currSon.Son.size(); int nature = TreeNode.NODE_NATURE_NORMAL;
TreeNode tn = new TreeNode(currSon.name.getValue(), " ", canRecruit(currSon), children > 0, null, nature);
nodes.add(tn); } }
The code fragment in Figure 61 initializes a new TreeNode object for each child soldier node with the soldier's name, whether this node is generatable (based on whether the soldier is of recruitable age), and whether this node is expandable (based on the number of children the soldier has). This call to the TreeNode() constructor does not initialize the tree node with a description (""), nor does it provide any child nodes. Once each new TreeNode object is instantiated, the code adds it to a Java Vector (nodes). When getNodes() has generated TreeNode objects for all child nodes, it copies the contents of this vector into a tree-node array with the following code:
TreeNode[] tn = new TreeNode[nodes.size()]; System.arraycopy(nodes.toArray(), 0, tn, 0, nodes.size());
The getNodes() method returns this tree-node array to getTreeNodes(), which in turn returns this array to its calling program, Business Object Wizard. Business Object Wizard uses this new tree-node array to display the expanded contents of the Uulius node, as Figure 62 shows.
Figure 62.
Expanding the Uulius source node
When a node has a "file" nature, the user can associate an operating system file with the node. Business Object Wizard indicates that a node has a file nature by activating the Associate files menu item when the user right-clicks the node name. For information about how to use this menu item, see Associating an operating-system file.
A file-nature node has its polymorphicNature member variable set to the NODE_NATURE_FILE node-nature constant. This constant is defined in the ODKConstant interface (which the TreeNode class implements). The following table shows the initialization that the TreeNode object requires for it to function as a file-nature node:
TreeNode member variable | Value |
---|---|
polymorphicNature | NODE_NATURE_FILE |
isExpandable | false |
nodes | null |
isGeneratable | false (usually) |
In the sample Roman Army ODA, the ArmyAgent4 class implements a getNode() method that supports file-nature nodes. This sample allows the user to associate a file with a source node for any node that represents a soldier that is at least 28 years old (the default minimum age) and has no children of his own. The code for this version of getNode() is almost identical to the code in Figure 61. The only difference is in the assignment of the value to the polymorphicNature member variable. Instead of assigning the NODE_NATURE_NORMAL constant to all nodes, the ArmyAgent4 version of getNode() uses the following code line to set the node nature to NODE_NATURE_FILE if the node represents a soldiers at least 28 years old and having no children:
int nature = m_allowAdoption && canAdopt(currSon) ? TreeNode.NODE_NATURE_FILE : TreeNode.NODE_NATURE_NORMAL;