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

Look in-depth at Java-to-XML translation

Jato, a new open source Java/XML translator, provides an effective solution to interfacing Java applications and XML documents. In Part 1 of this series, I provided a quick overview of using Jato to transform from Java-to-XML and XML-to-Java. In Part 1 I also discussed Jato's key features, and its important classes and interfaces.

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

This article, the second in a series of three, moves from the general to the specific by examining Java-to-XML transformations in detail. In that context, we'll see examples illustrating how to:

  • Utilize macros to reduce Jato scripting
  • Use the Jato debugger to understand script execution
  • Perform conditional XML generation
  • Obtain an understanding of script execution using debug statements
  • Recursively transform hierarchically structured object systems
  • Invoke Java methods polymorphically
  • Invoke custom Jato functions

The techniques covered in this article represent ideal generating scripts to persist application-configuration information, serialize Java objects for long-term storage, and dynamically generate XML for viewing in Web browsers. As a vehicle for discussing these topics, we will develop a Jato script that converts a directory structure as represented by the java.io.File class into XML. The completed script will perform the following tasks:

  1. Set a path attribute in the XML document's root node that specifies the path of the base directory.
  2. Write permissions, modification date, and size information about each file and directory contained in the base directory.
  3. Recursively write information about the contents of the base directory. Thus, each directory found in the base directory will be examined; moreover, information about each of its files and directories will be generated.

The following listing provides sample output from the finished script:

<?xml version="1.0" encoding="UTF-8"?>
<root path="E:\ARTICLES\jw-jato-2\examples">
   <dir name="examples" modified="Mon, Jan 15, '01" permissions="rw">
      <dir name="step-1" modified="Tue, Jan 16, '01" permissions="rw">
         <file size="1403" modified="Sat, Mar 17, '01" permissions="rw">
             java-to-xml.xml
         </file>
         <file size="3620" modified="Fri, Feb 09 '01" permissions="rw">
             FileToXML.java
         </file>
      </dir>
   </dir>
</root>

Like many XML documents, our example can generate deeply nested XML elements. With recursive macro calls, we can handily generate and process such documents. One of the difficulties with recursive programs is tracking what the program is currently processing. Jato provides an excellent means for tracking program execution with the <Jato:debug> tag. We'll see macros and debugging next.

Note: At the time of this writing, Jato is in beta 2, with tremendous development work being piled into it. Occasionally, a change is made that will break backwards compatibility. To ensure the article examples work properly, the distribution will include all the samples from this series.

<Jato:macro> and <Jato:debug> statements

Jato macros, a collection of Jato and regular XML tags, provide a powerful facility to reduce script line count and enable recursive algorithms. In Jato, you invoke macros using the <Jato:inline> tag and declare them in a <Jato:macros> section using <Jato:macro> tags as shown in Listing 1:

Listing 1: Jato script demonstrating macros and debug

1. <Jato:defs xmlns:Jato='http://jato.sourceforge.net'>
2.    <Jato:inline macro='printMsg'/> 
3.
4.    <Jato:macros>
5.       <Jato:macro name='printMsg'>
6.          <Jato:debug>Invoked macro</Jato:debug> 
7.       </Jato:macro>
8.    </Jato:macros>
9. </Jato:defs>

The script in Listing 1 performs the following:

  • Line 2: Invokes the 'printMsg' macro, the only top-level line of script. Think of it as being the same as a one-line main() method that invokes another method.
  • Line 4: The <Jato:macros> section contains all the macro definitions as delineated by the <Jato:macro> tags.
  • Line 5: Declares the 'printMsg' macro.
  • Line 6: The macro consists of a single line that prints the message "Invoked macro." Later examples will demonstrate other capabilities of the <Jato:debug> tag.

This script does not generate an XML document, rather it simply prints the message "Jato Debug: Invoked macro" to the standard output stream. The script can be easily run without writing any Java code by using the org.jato.JavaToXml script runner:

> java org.jato.JavaToXml -f macro-demo.xml -nop
JATO Debug: Invoked macro

In the code above, the -f flag instructs the script runner to use the Jato script called macro-demo.xml instead of the default script name, java-to-xml.xml. The -nop option instructs the interpreter to suppress printing the generated XML document.

Let's look at a few notes about invoking macros:

  • Macros may be invoked within other macros
  • A macro can be recursively invoked
  • A macro can contain just about any Jato script or XML tags (which must be well-formed within the macro definition)
  • Macros can be invoked from practically anyplace in a Jato script

Establish a script structure

Using Jato macros, we can develop the structure for our Jato script that will generate an XML document describing the contents of a filesystem. Before developing the Jato script, let's examine a Java program that iterates over the contents of a java.io.File. The program will provide several useful insights for developing the structure of our Jato script.

The process of traversing a hierarchical file structure is easily expressed using a recursive algorithm. The ListDir class implements such a recursive algorithm in Listing 2. Notice the recursive call in ls() at line 17 when the File being inspected is a directory:

Listing 2: Recursive directory traversal in Java

1.  public class ListDir {
2.     public static void main(String args[]) {
3.        //get the root node for iterating
4.        File root = new File(System.getProperty("user.dir"));
5.
6.        //iterate contents of directory
7.        ls(root); 
8.     }
9.
10.    static void ls(File f) { 
11.       System.out.println("Traversing directory: " + f);
12.       File list[] = f.listFiles();
13.
14.       for (int i=0; i<list.length; i++) {
15.          if (list[i].isDirectory()) {
16.             //recurse file
17.             ls(list[i]);
18.          } else {
19.             System.out.println("File: " + list[i]);
20.          }
21.       }
22.    }
23. }

The code in Listing 3 implements a Jato script that performs the same recursive directory iteration except it will output <dir> and <file> tags describing directories and files, respectively:

Listing 3: Jato script for recursively traversing a directory

1.  <?xml version='1.0' encoding='UTF-8' ?>
2.  <Jato:defs xmlns:Jato='http://jato.sourceforge.net'>
3.     <Jato:translate key='root'>
4.        <Jato:inline macro='dir'/>
5.     </Jato:translate>
6.
7.     <Jato:macros>
8.     <Jato:macro name='dir'>
9.        <dir>
10.          <Jato:debug print-object='true'>Transforming directory</Jato:debug>
11.          <Jato:attribute name='name' property='name'/>
12.          <Jato:invoke method='listFiles'>
13.             <Jato:if property='directory'>
14.                <Jato:cmt>Recursive call to 'dir' macro</Jato:cmt>
15.                <Jato:inline macro='dir'/>
16.                <Jato:else>
17.                   <Jato:debug print-object='true'>Transforming file</Jato:debug>
18.                   <file>
19.                      <Jato:text property='name'/>
20.                   </file>
21.                </Jato:else>
22.             </Jato:if>
23.          </Jato:invoke>
24.       </dir>
25.    </Jato:macro>
26.    </Jato:macros>
27. </Jato:defs>

The generated XML document appears as:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <dir name="examples">
      <dir name="step-1">
         <file>java-to-xml.xml</file>
         <file>FileToXML.java</file>
      </dir>
   </dir>
</root>

The script in Listing 3:

  • Line 3: Gets the Java object keyed by 'root' from the translator helper. This will be a java.io.File object.
  • Line 4: Transfers control to the 'dir' macro specified at line 8.
  • Line 9: Inserts a <dir> tag into the output document. Remember any non-Jato tags are inserted into the destination document.
  • Line 11: Sets the 'name' attribute to the value of the current object's name property. In this case, the File method getName() is invoked and the returned String is used as the property value.
  • Line 12: Invokes the method File[] listFiles() on the current object (a File object). The script inside the <Java:invoke> tag runs on each element in the array. During this evaluation the element being evaluated becomes the current object.
  • Line 13: Jato conditional checks the state of the directory property. The directory property maps to the boolean isDirectory() method of the File class. If the property is true, the script in the <Jato:if> tag is evaluated; otherwise, the <Jato:else> tag at line 15 is evaluated. Note that Jato also supports a <Jato:elseif> tag.
  • Line 15: Recursively invokes the 'dir' macro on the current File object and current <dir> tag. This is the key step to building a hierarchical XML document that mirrors the hierarchical structure of the filesystem.
  • Line 10 and 17: The <Jato:debug> tag prints out information about the current interpreter state. print-object='true' tells Jato to print the current object's toString() value. In addition, you may specify a 'print-elt' attribute that will print the state of the current XML tag being manipulated. In this case, the interpreter prints out messages that appear as:

    JATO Debug: Transforming directory:
       Object = E:\ARTICLES\jw-jato-2\examples\step-1
    JATO Debug: Transforming file:
       Object = E:\ARTICLES\jw-jato-2\examples\step-1\java-to-xml.xml
    JATO Debug: Transforming file:
       Object = E:\ARTICLES\jw-jato-2\examples\step-1\FileToXML.java
    
  • Line 19: The <Jato:text> tag sets text for the current <file> XML tag. The value of the text is obtained by getting the current object's name property as seen in line 11.

Besides the fact that the Jato script generates an XML document, it performs essentially the same task as the Java program in Listing 2. Table 1 provides a line-by-line comparison of the Java program and the Jato script with the XML element-generation portions removed. As the table shows, the similarities are striking. As the series develops, you will notice that Jato integrates with any set of Java objects and XML schema while expressing this integration using a minimal amount of code. Note: In Table 1, System.out.println() is shortened to just println().

JatoJava

<Jato:translate key='root'>

<Jato:inline macro='dir'/>

</Jato:translate>

File root = new File(...)

ls(root);

<Jato:macro name='dir'>static void ls(File f) {
   <Jato:debug>Transforming directory</Jato:debug>println("Iterating directory: " + f);
   <Jato:invoke method='listFiles'>

File list[] = f.listFiles();

for (int i=0; i<list.length; i++) {

   <Jato:if property='directory'>   if (list[i].isDirectory()) {
   <Jato:inline macro='dir'/>      ls(list[i]);

   <Jato:else>

      <Jato:debug>Transforming file</Jato:debug>

   </Jato:else>

</Jato:macro>

   } else {

      println("File: " + list[i]);

   }

}

Table 1. Comparison of Java program and Jato script

Add content XML elements

The Jato script in Listing 3 specifies the name of &ltdir> elements using the name attribute and the name of &ltfile> elements using text. The Jato interpreter maintains a reference to the currently generated XML element. Any content generated in a script is added to this current output XML element. Jato provides the &ltJato:attribute> tag to add attributes and the &ltJato:text> tag to add text.

To effectively understand how Jato maintains a reference to the current output element, we turn to the Jato debugger. Figure 1 shows the Jato debugger at the point before a name attribute is set on the current &ltdir> element. The Jato script appears in the top-left corner, while the output document appears in the top-right corner. Figure 2 shows the Jato debugger after the attribute has been added. Notice that in both cases the current element is highlighted.

Figure 1. Jato debugger shows the script before adding an attribute
Figure 2. Jato debugger after the script added an attribute
1 2 3 Page 1