Jato: The new kid on the open source block, Part 3

Translate XML documents into Java objects

Welcome back for Part 3 in this series on Jato, the open source, increasingly capable, XML/Java translator. In Part 1, I introduced Jato and how to perform XML-to-Java and Java-to-XML transformations. Since then Jato has experienced a complete redesign, a number of enhancements, and the addition of an interactive debugger. Part 2 focused on generating XML documents from complex Java object structures through an application that generated an XML document containing file system information. Part 2 further discussed macro writing, XML element and attribute creation, debug statements utilization, Java methods invocation, and Jato function writing.

Read the whole "Jato: The New Kid on the Open Source Block" series:

In Part 3, we turn our attention to XML-to-Java transformations and the conditional tags <Jato:if> and <Jato:else>, as well as Jato expressions, recursive XML traversal, constructors invocation, JavaBeans property settings, conditional parameter lists, and a little Swing.

Note: You may find the Jato Syntax Reference Guide useful for obtaining more detailed information on all the tags presented in this article.

Sounds exciting, so let's get started.

The new assignment

In Part 2 we transformed the hierarchical file-structure information contained in a File object into an XML document called site.xml. A partial listing of site.xml is shown is Listing 1:

Listing 1. Partial listing of site.xml

<?xml version="1.0" encoding="UTF-8"?>
<root path="E:\jato\examples">
   <dir name="examples" modified="1/15/01 10:11 PM" permissions="rw">
      <dir name="simple" modified="1/16/01 9:44 PM" permissions="rw">
         <file size="2745" modified="3/16/01 4:49 PM" permissions="rw">
               SimpleXmlToJava.java
         </file>
         <file size="2745" modified="3/16/01 4:49 PM" permissions="rw">
             SimpleXmlToJava.java
         </file>
      </dir>
   </dir>
   <file size="0" modified="3/17/01 10:38 AM" permissions="rw">
      file
   </file>
</root>

In our new assignment, we must create a Jato script that transforms site.xml into a Swing user interface. The finished application is shown in Figure 1.

Figure 1. JatoTree finished appearance

The user interface makes heavy use of the JTreeTable class source code provided in "Creating TreeTables: Part 2," Scott Violet and Kathy Walrath (java.sun.com).

Before jumping into the Jato script that transforms site.xml, let's lightly explore Jato expressions, a feature required to get an A on our assignment.

Jato expressions

Jato introduced expressions with the release of Beta 2 in March 2001. Expressions provide a compact syntax for obtaining current interpreter state information and evaluating logic that would require a large amount of Jato script. Jato uses expressions to perform conditional checks for <Jato:if> and <Jato:elseif> tags, generate debug statements, set the current object, manipulate the current input and output XML elements, construct variable names, and cause surly programmers to perform random acts of kindness. In this aritcle, I'll quickly introduce expressions, while a future article will deal with this subject in detail.

As an introduction to Jato expressions, consider the following debug statement:

<Jato:debug>
    "Current system time: " ~ class.System.currentTimeMillis()
</Jato:debug>

This statement will print the following message to the console:

Jato Debug: Current system time: 985279624868

Ah, the funky and useful things Jato expressions can accomplish. In this case, the expression engine parses this expression as:

  • Operator: The concatenation operator ('~').
  • Left operand: Literal string ("Current system time: ").
  • Right operand: A chained functional expression:
    • class: Instructs the expression engine to evaluate the expression between the two dots to obtain a class name. Fully qualified names are not required if the class resides in an imported package. The expression engine then invokes the method specified after the second dot. Parameters may be passed by placing comma-separated expressions between the parenthesis.
    • System: Specifies the simple class name for java.lang.System as an unquoted literal string, but the engine lets developers write complex expressions that invoke a method on the current object or read an XML attribute. The expression engine allows single, double, and unquoted literals.
    • currentTimeMillis(): Invokes the static method currentTimeMillis() on the System class passing zero parameters.

Using the return value from one expression to invoke another expression is called functional chaining -- similar to method chaining in Java. For instance, Java allows the following statement to determine the index of the first occurrence of the string "numb":

int index = new String("Comfortably Numb").toLowerCase().indexOf("numb");

This statement creates a String instance, converts it to lowercase, and then determines the index of the word "numb". The return value from the previous method invokes the next method. The same line of Java code could be spread over multiple lines as:

String title = new String("Comfortably Numb");
title = title.toLowerCase();
int index = title.indexOf("numb");

Unlike Java, where method chaining equals a luxury but not a necessity, Jato often requires function chaining to accomplish a given task. If you are a little confused by the current example, the examples in the following sections will provide additional expression examples.

Establish a script structure

To design and write the script, you must understand how to create JTreeTable components. The JTreeTable is a multiple column tree component that combines a JTree and JTable. The component uses a TreeTableModel as the data model; we will employ a concrete implementation called the FileSystemModel. The FileSystemModel maintains a list of FileNode objects, which is an abstract base class that defines methods for obtaining a file name, type, size, and last modified date. The code in Listing 2 creates a JTreeTable with a root and two descendants. Figure 2 shows the appearance of the JTreeTable when configured using Listing 2:

Listing 2. Example JTreeTable creation

import tree.*;   //the JTreeTable package
//create a root node and add file nodes
DirNode root = new DirNode();
//create a directory node and add to root
DirNode node1;
node1 = new DirNode("level 1", "rw");
root.addChild(node1);

//create a content node (leaf) and add to node 1
ContentNode node2;
node2 = new ContentNode("level 2", "rw");
node2.setSize(12345);
node2.modifiedOn(new Date());
node1.addChild(node2);
//use the root node and use it to create the data model and tree
JTreeTable treeTable = new JTreeTable(new FileSystemModel(root));
Figure 2. Appearance of JTreeTable

Notice that all JTreeTable classes reside in the tree package. The code fragment in Listing 2 uses two concrete implementations of the FileNode class:

  • DirNode: Represents a directory that can contain other directory and file nodes. The DirNode provides the following constructors:
    • public DirNode() //used to create root nodes.
    • public DirNode(String name, String perms).
  • ContentNode: Represents a data file and is always a leaf node. ContentNode defines a single constructor: public ContentNode(String name, String perms).

The code fragment makes use of two methods defined in the FileNode parent class:

  • public void setSize(int size): Implemented in the ContentNode class to set the byte size. The DirNode class ignores the method and calculates a size by summing the sizes of its child nodes.
  • public void modifiedOn(Date mod): Sets the file system object's last modified date. Notice it requires a java.util.Date instance.

Putting it all together, our script needs to perform the following:

  • Create a root DirNode
  • Iterate each element in site.xml
  • Instantiate a DirNode instance for each <dir> tag and add it to the parent DirNode:
    • Instantiate a ContentNode instance for each <file> tag and add it to the parent DirNode
    • Create a Date object using the value specified in the modified attribute and invoke the node's modifiedOn() method
    • For each <file> element, we also need to invoke the setSize() method passing size attribute's value
  • Create a FileSystemModel using the root node and instantiate the tree

The code in Listing 3 establishes the structure for the Jato script:

Listing 3. Template for script to create JTreeTable

1.  <Jato:jato-defs xmlns:Jato='http://www.jdom.org/jato'>
2.     <Jato:import>java.util.Date</Jato:import>
3.     <Jato:import>tree.*</Jato:import>
4.
5.     <Jato:object type='DirNode' publish='root'>
6.        <Jato:inline macro='dir'/>
7.        <Jato:inline macro='file'/>
8.     </Jato:object>
9.
10.    <Jato:macros>
11.       <Jato:macro name='dir'>
12.          <Jato:on-elt name='dir'>
13.             <Jato:debug print-elt-in='true'>Create dir node</Jato:debug>
14.
15.             <!-- process child elements using recursive macro calls -->
16.             <Jato:inline macro='dir'/>
17.             <Jato:inline macro='file'/>
18.          </Jato:on-elt>
19.       </Jato:macro>
20.
21.       <Jato:macro name='file'>
22.          <Jato:on-elt name='file'>
23.             <Jato:debug print-elt-in='true'>Create file node</Jato:debug>
24.          </Jato:on-elt>
25.       </Jato:macro>
26.    </Jato:macros>
27. </Jato:jato-defs>

The template performs the following actions:

  • Lines 2 and 3: Imports the Date class and all the JTableTree classes in the tree package.
  • Line 5: The <Jato:object> tag creates Java objects. In this case, the type attribute specifies the DirNode class using the default constructor. The publish attribute instructs the Jato interpreter to invoke the helper's publish() method and pass the directory node and the identifying key value of 'root'.
  • Line 6: Invokes the 'dir' macro, which will process the root's <dir> child elements.
  • Line 7: Invokes the 'file' macro, which will process the root's <file> child elements. The directory nodes will be created and added to the tree before the file nodes since the 'dir' macro call appears before the 'file' macro. This will cause the JTreeTable to display the directory nodes before the file nodes.
  • Line 11: Declares the start of the 'dir' macro.
  • Line 12: The <Jato:on-elt> tag iterates over the child elements of the current input XML elements, looking for tags named <dir>. For each match, the child element becomes the current input XML element and the script inside the <Jato:on-elt> tag is interpreted. When Jato initially calls this macro, the root directory node will be the current object.
  • Line 16: Recursively invokes the 'dir' macro. This will cause the <dir> elements contained in the current input XML element to be processed. The recursion will cease when the <Jato:on-elt> tag does not find a match. This recursive structure implements a depth-first search.
  • Line 17: Calls the 'file' macro to process any <file> child tags of the current input XML element.
  • Line 21: Declares the start of the 'file' macro.
  • Line 22: Searches the current input XML element for <file> nodes.

With the script structure established, we are ready to instantiate objects and invoke a few methods.

The <Jato:invoke>, <Jato:constructor>, and <Jato:param> tags

Because the 'dir' and 'file' macros perform very similar steps, in this section we will concentrate on the 'dir' macro, while the 'file' macro will be presented later. When implementing a script, keep in mind the objects with which a tag interacts to implement its behavior. Jato provides three reference objects for use by all script tags and expressions:

  • Current object: The Java object currently under manipulation. This reference is changed by many Jato tags, including <Jato:object>, <Jato:translate>, and <Jato:invoke>. A tag establishes the current object reference for its enclosed script. Of course, one of its enclosed script tags can set a different reference for its enclosed script tags.
  • Current input element: The XML element from the input document currently being processed by the interpreter. The <Jato:on-elt> and <Jato:translate> tags change this reference for their enclosing scripts.
  • Current output element: The XML element to which newly created XML elements and attributes will be added.

Implementing the 'dir' macro requires three tags:

  • <Jato:invoke>: Invokes a method on the current object. The exact form of the method is determined at runtime based on the <Jato:param> value types enclosed by the script and the current object's class.
  • <Jato:param>: Specifies a parameter for <Jato:invoke> and <Jato:constructor> tags. Jato lets you obtain parameter values from the current input element, method return values, and property values from the current object, static method return values, Jato variables, helper controlled objects, Jato functions, or expressions. If there is a value in the system, Jato provides a mechanism to obtain it.
  • <Jato:constructor>: Describes the constructor to use when instantiating an object. Jato uses the default constructor unless a <Jato:constructor> script tag is defined. Line 5 in Listing 3 did not contain a <Jato:constructor> tag, so Jato created the DirNode instance using the default constructor.

Listing 4 presents a 'dir' macro implementation. We first see the initial macro implementation as a single, monolithic macro. After examining the initial implementation, we break the macro into three macros to facilitate code sharing with the 'file' macro:

Listing 4. The 'dir' macro

1.  <Jato:macro name='dir'>
2.     <Jato:on-elt name='dir'>
3.        <Jato:invoke method='addChild'>
4.           <Jato:param type='DirNode'
5.                       param-type='FileNode'>
6.              <Jato:constructor>
7.                 <Jato:param type='string' path='@name'/>
8.                 <Jato:param type='string' path='@permissions'/>
9.              </Jato:constructor>
10.             <Jato:invoke method='modifiedOn'>
11.                <Jato:param type='Date'>
12.                   <Jato:constructor>
13.                      <Jato:param type='string' path='@modified'/>
14.                   </Jato:constructor>
15.                </Jato:param>
16.             </Jato:invoke>
17.          </Jato:param>
18.       </Jato:invoke>
19.
20.       <!-- process child elements using recursive macro calls -->
21.       <Jato:inline macro='dir'/>
22.       <Jato:inline macro='file'/>
23.    </Jato:on-elt>
24. </Jato:macro>

We added Jato script to the initial 'dir' macro implementation that performs the following tasks:

  • Line 3: Invokes the addChild(FileNode) method on the current object, which is the root node for the initial macro call. Jato determines the method to invoke based on the parameter list as defined by its child script tags.
  • Line 4: Defines the single parameter for the addChild() method. Since the type is not a primitive Java type, this parameter asks Jato to create a DirNode instance. This tag changes the current object for all of the enclosed tags between lines 10-16. The current object switch occurs after the <Jato:constructor> tag since the DirNode object becomes live after creation.
  • Line 5: The param-type attribute specifies the type in the actual method declaration as would be seen in the Javadoc. The param-type is required when the type being passed to a method is a subclass or implementation of the method declaration class or interface, respectively. Notice that line 11 does not contain a param-type attribute because the modifiedOn() method takes a Date reference and a Date object is being passed as the parameter. On line 5, however, DirNode is a FileNode subclass and the param-type attribute is required. This requirement may relax in future versions of Jato but it will always be more efficient to specify that attribute when passing subclasses of declared parameter types.
  • Line 6: Instructs the Jato interpreter to use a DirNode nondefault constructor. The tag contains two parameter tags that specify string types so the required constructor declaration must be:

       DirNode(String, String)
    
  • Lines 7 and 8: Specify two string parameters with values obtained from the name and permissions attributes of the current input element.
  • Line 10: Invokes the modifiedOn() method on the newly created DirNode object. Remember, all child tags of the <Jato:param> tag in line 4 will use newly created DirNode as the current object. On the first invocation, the root node is assigned as the current object except for lines 10-16, which use the DirNode from line 4.
  • Line 11-15: Creates a Date object using the current input element's modified attribute as the single parameter to the constructor:

        Date(String)
    

If this script seems too verbose and clunky, then you are a prime candidate for expressions, the topic of Part 4. In the meantime, the code in Listing 5 breaks the 'dir' macro functionality into three macros to prepare for implementing the 'file' macro:

Listing 5. A 'dir' macro implementation ready for the 'file' implementation

1.  <Jato:macro name='dir'>
2.     <Jato:on-elt name='dir' action='Jato:invoke' method='addChild'>
3.        <Jato:param type='DirNode'
4.                    param-type='FileNode'>
5.           <Jato:inline macro='ctor'/>
6.           <Jato:inline macro='modified'/>
7.
8.           <!-- process child elements using recursive macro calls -->
9.           <Jato:inline macro='dir'/>
10.          <Jato:inline macro='file'/>
11.       </Jato:param>
12.    </Jato:on-elt>
13. </Jato:macro>
14.
15. <Jato:macro name='ctor'>
16.    <Jato:constructor>
17.       <Jato:param type='string' path='@name'/>
18.       <Jato:param type='string' path='@permissions'/>
19.    </Jato:constructor>
20. </Jato:macro>
21.
22. <Jato:macro name='modified'>
23.    <Jato:invoke method='modifiedOn'>
24.       <Jato:param type='Date'>
23.          <Jato:constructor>
25.             <Jato:param type='string' path='@modified'/>
26.          </Jato:constructor>
27.       </Jato:param>
28.    </Jato:invoke>
29. </Jato:macro>

Although mainly a cut-and-paste exercise, Listing 5 made several significant script-tag modifications:

  • Line 2: Removed the invoke tag by adding an action attribute to the <Jato:on-elt> tag. Many scripts wish to perform a single task once an element is matched using <Jato:on-elt>. To reduce the lines of code, the <Jato:on-elt> tag checks for an action attribute that specifies a fully qualified script tag name. Upon finding an element match, the script tag treats the tag as the element type specified in the action attribute. Now the <Jato:on-elt> tag has all the attributes and child tags of the previous implementations <Jato:on-elt> and <Jato:invoke> tags.
  • Line 5: The constructor call was moved to a 'ctor' macro ("ctor" is short for constructor, but you may name the macro whatever you wish). This points towards a neat Jato feature: Jato will inspect macro calls and conditional statements while searching parameter and constructor tags. The 'file' macro will utilize the same macro.
  • Line 6: Moved the modifiedOn() method invocation tag to a 'modified' macro.

A key point to notice about the code changes: the current object and current element references remain unchanged during a macro call. Although Jato does not actually inline the XML code when compiling scripts, the use of inline as the element name for calling macros provides the impression that the code behaves as if it were inlined. (As a historic note, the original Jato interpreter actually cloned the macro XML elements and inlined them.)

The <Jato:if>, <Jato:else>, and <Jato:property> tags

To finish the script, we need to implement the 'file' macro, which performs a few tasks differently than the 'dir' macro:

  1. Because <file> elements are always leaf elements, they correspond to a ContentNode object in our JTreeTable implementation and will not contain any recursive macro calls.
  2. The first constructor argument is specified as the element text instead of as an attribute. For example, consider the following two partial <dir> and <file> elements from the site.xml file:

        <dir name="file">
        <file size="2745">JatoTree.java</file>
    
  3. ContentNode instances have a size property that needs to be set using the size attribute.

Listing 6 shows the 'file' macro and the updated 'ctor' macro:

Listing 6. The new and improved 'file' and 'ctor' macros

1.  <Jato:macro name='file'>
2.     <Jato:on-elt name='file' action='Jato:invoke' method='addChild'>
3.        <Jato:param type='ContentNode' param-type='FileNode'>
4.            <Jato:inline  macro='ctor'/>
5.            <Jato:property name='size' path='@size'/>
6.            <Jato:inline macro='modified'/>
7.        </Jato:param>
8.     </Jato:on-elt>
9.  </Jato:macro>
10.
11. <Jato:macro name='ctor'>
12.    <Jato:constructor>
13.       <Jato:if condition="in.getName().equals('dir')">
14.          <Jato:param type='string' path='@name'/>
15.       </Jato:if>
16.       <Jato:else>
17.          <Jato:param type='string' path='.'/>
18.       </Jato:else>
19.       <Jato:param type='string' path='@permissions'/>
20.    </Jato:constructor>
21. </Jato:macro>

The file macro seems pretty tame compared to the new 'ctor' macro. Some of the 'ctor' macro's complexity derives from my desire to demonstrate <Jato:if> and <Jato:else> tags, expressions, and conditional parameter lists. Taking one step at a time, the code in Listing 6 performs the following tasks:

  • Line 3: Creates a ContentNode as a parameter to the current object's addChild() method, where the current object is a DirNode when the macro begins executing.
  • Line 5: The <Jato:property> tag sets a JavaBeans property for the current object. Jato provides many mechanisms for obtaining the new property value. In this case, the new property value is pulled from the current element's 'size' attribute. The current object at line 5 is the ContentNode created at line 3 because the <Jato:property> tag is a child of the <Jato:param> tag.
  • Line 13-18: The <Jato:if> tag performs a check on the current input element to determine how to retrieve the first constructor parameter. <Jato:if> and <Jato:elseif> tags evaluate an expression specified using the condition attribute. If it returns true, the enclosing script is evaluated. The condition performs the following evaluation:
    1. in.: Refers to the current input element that is an object of type org.jdom.Element. The string between the '.' and the '(' is considered to be a method call on the Element object and invoked using reflection. As you may have guessed, the current object and current output element are referred to as this. and out., respectively.
    2. getName(): Instructs the expression engine to invoke the Element method getName(), which takes zero parameters. Any parameters would be specified using comma separated expressions.
    3. .equals('dir'): Invokes the equals() method on the string object returned by the getName() method. This is another function chaining example. The 'dir' parameter is evaluated as a single quoted-literal value but could have been any valid Jato expression.
  • Line 17: For tags not equal to <dir>, the first constructor parameter is obtained by getting a string parameter from the current input element's text value.

Here's the final script.

Implement the Integrator class and main() method

With the script out of the way, we next tackle the user interface creation and display. You can create the tree and add the root tree node within the script, but using Java code to perform these tasks simplifies the Jato script and scales well as problem complexity increases. For this application, Jato will transform the XML data into tree nodes and the Java code will create the actual user interface. Determining the dividing point between scripted and compiled code always proves an interesting question. The Java application code is presented in Listing 7:

Listing 7. The JTreeTable application code

1.  public class JatoTree {
2.         public static void main(String[] args)  throws Exception {
3.            new JatoTree();
4.         }
5.
6.         public JatoTree() throws JatoException, IOException, JDOMException {
7.            //create a helper, get the root node, and build the tree
8.            NodeIntegrator integrator = new NodeIntegrator();
9.            FileNode root = integrator.getRootNode();
10.     JTreeTable treeTable = new JTreeTable(new FileSystemModel(root));
11.
12.     //create a frame, add JTreeTable, size window, and other GUI stuff here
13.   }
14. 
15. //Interface point between Jato and Java application code
16. class NodeIntegrator implements JatoIntegrator {
17.   DirNode fRoot;
18. 
19.   FileNode getRootNode() throws JatoException, IOException, JDOMException
20.    {
21.       //load the default tags into the builder
22.       ScriptBuilder.loadDefaultJatoDefs();
23. 
24.       //create script document from the static string class variable
25.       InputStream xmlIS = new FileInputStream("xml-to-java.xml");
26.       Document scriptDoc = new SAXBuilder().build(xmlIS);
27.       xmlIS.close();
28. 
29.       //create the script and interpreter with no debug information
30.       JatoScript script = ScriptBuilder.createJatoScript(scriptDoc.getRootElement(), 
31.                                                             false);
32.       Interpreter jato = new Interpreter(script, this);
33. 
34.       //read in the XML input document for the transformation
35.       xmlIS = new FileInputStream("site.xml");
36.       Document xmlIn = new SAXBuilder().build(xmlIS);
37.       xmlIS.close();
38. 
39.       //perform the transformation using the root elements of the input document
40.       //the script does not produce any output so pass in null for output element
41.       jato.transform(xmlIn.getRootElement(), null);
42. 
43.       //return the root node which was set in the publish method
44.       return fRoot;
45.    }
46. 
47. 
48.    /**
49.     * Method invoked by interpreter when Jato script tag contains a 50.     * 'publish' attribute.
51.     *
52.     * @param key -- value of the 'publish' attribute in the Jato script
53.     * tag. This value is used to distinguish the category of object being published.
54.     * @param obj -- the object being published
55.     * @param state -- one of CREATED_STATE or INITIALIZED_STATE.
56.     */
57.    public void publish(String key, Object obj, int state)  {
58.       DirNode node;
59. 
60.       if (key.equalsIgnoreCase("root")) {
61.          if (state == CREATED_STATE) {
62.             fRoot = (DirNode)obj;
63.          }
64.       } else {
65.          throw new IllegalArgumentException("Unknown key: " + key);
66.       }
67.    }
68. 
69.    /**
70.     * Method invoked by interpreter when Jato script tag requests a Get command.
71.     *
72.     * @param key - the value of the 'get' attribute or 'key'
73.     * attribute for &ltJato:get> tags.
74.     * @param parms - the parameters specified for the Get command using
75.     * &ltJato:param> tags. The tags must be specified using a
76.     * name attribute.
77.     */
78.    public Object getObject(String key, Properties parms) {
79.       throw new IllegalStateException("getObject() not supported by integrator");
80.    }
81. }

Jato has undergone a few design changes since Part 2 was published. The changes that affect the code in Listing 7 deal with creating Jato interpreters and providing an integration point between Jato and the Java application code. Specifically, executing a Jato script now involves these tasks:

  1. Implement the JatoIntegrator interface: This interface replaces the InterpreterHelper interface and contains just two methods: getObject() and publish(). These two methods were part of the InterpreterHelper interface and their use remains unchanged.
  2. Create a JatoScript instance: The new design encapsulates the information contained in a Jato script within the JatoScript class and provides the ScriptBuilder class for constructing JatoScript instances. This change allows scripts to be safely executed by multiple threads concurrently.
  3. Create an Interpreter instance: Interpreters are now instantiated using a JatoScript instance and an optional JatoIntegrator instance. A single JatoScript instance may be shared by multiple Interpreter instances.
  4. Execute the script using the interpreter's transform() method: The XML input and output elements to be utilized during the transformation are passed in as transform() parameters. The helper provided the XML elements in the previous design and this led to needless helper class complications and serious limitations.

Having said all that, Java code written to the two Jato designs are very similar; consequently, most of Listing 7 should look similar to previous Jato examples. This is the first application, however, that uses Jato to read configuration data for a larger purpose. In that regard, the JatoTree class represents a stereotypical Jato scenario. Some interesting code points are:

  • Lines 8 and 9: All Jato code is removed from the main application and placed in the integrator class, NodeIntegrator. The integrator class's only visible role is to "get" the root element.
  • Line 16: The integrator class implements the JatoIntegrator interface.
  • Lines 21-44: The getRootNode() method does all the heavy lifting for the application. The default script tags are loaded into the Jato environment at line 22. The method ends by returning the DirNode root object created by our script and published using the key of "root". The instance variable assignment takes place at line 62 during script execution.
  • Line 41: The transform() method is invoked by passing the site.xml root element in as the input element and no output element.

All that is left is the simple command:

c:\> java JatoTree :-)

The smiley face is optional.

Conclusion

Could this assignment have been simplified by providing more JavaBeans methods, using consistent file and directory tag structures, and performing more data conversions in the node classes? Or thought of differently, could the problem be rigged to make creating a Swing-based GUI look brain-dead simple? Sure, but where is the fun in making examples simple; especially when projects involving actual requirements are rarely simple and trivial. I will leave the cooked demos to the proprietary vendors and keep the real-world tasks for the open source community.

This article provides a more in-depth look at using Jato to perform XML-to-Java transformations. We jazzed it up a bit by creating a nifty Swing user interface using information stored in the pedestrian sites.xml file. The solution demonstrates instantiating objects, invoking complex constructors and methods, evaluating conditional parameter lists, using recursive macro calls to traverse hierarchical XML structures, and setting JavaBeans properties. Finally, the article provides an initial sighting of the all-powerful Jato expression engine. But that, as they say, is a story for another day.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies