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

Jato provides multiple forms for most tags. The Jato Reference Guide calls these different forms tag formats. Listing 3 uses the attribute-tag format called "Property Format" that obtains the new attribute value using a JavaBeans property from the current object. A property formatted tag will always contain a property attribute, which specifies the JavaBeans property to get or set using the current object. Thus, the following &ltJato:attribute> tag sets the path attribute on the root element using the absolutePath beans property:

<Jato:translate key='root'>
   <Jato:attribute name='path' property='absolutePath'/>
   <Jato:inline macro='dir'/>
</Jato:translate>

In this case, the JavaBeans property value absolutePath maps to the File getAbsolutePath() method. The &ltJato:text> tag supports the same formats as the &ltJato:attribute> tag. Following the coding pattern, the following tag sets the text on the current output element using the name property:

 
<file>
  <Jato:text property='name'/>
</file>

The "Invoke Format" provides a format for obtaining values when a class does not provide a beans method. The "Invoke Format" specifies the following:

  • invoke attribute: Specifies the method name to invoke
  • class attribute: Optional attribute that specifies the class on which to invoke the method when calling static methods
  • &ltJato:param> child tags: Optional tags that specify the parameters to pass to the method

The following &ltJato:attribute> tag sets the size attribute on the current <file> output element by invoking the File method length() on the current Jato object:

<file>
   <Jato:attribute name='size' invoke='length'/>
</file>

The length() method does not specify a class attribute or &ltJato:param> tags since it is not static and takes no parameters. For more information on other tags and formats, see the Jato Reference Guide.

Invoke Jato functions

So far, Jato has provided the built-in support for creating elements and adding content to the output document. To generate the permissions attribute, the implementation of a Jato function is required. Remember from the problem description, the file read and write permissions must have the form:

<file permissions='rw'>

In the permissions attribute, 'r' means the file has read permissions and 'w' means the file has write permissions. The File class provides the two methods: canRead() and canWrite(), but they return boolean values. Our task: construct a Jato function that will turn the boolean values for read and write permissions into one of the strings 'rw', 'r', 'w', or empty string for no permissions.

To invoke the Jato function, use:

<Jato:attribute name='permissions' function='file-perms'>
   <Jato:param name='read' invoke='canRead'/>
   <Jato:param name='write' invoke='canWrite'/>
</Jato:attribute>

This script invokes the Jato function 'file-perms' and passes two parameters: 'read' and 'write', which you obtain by invoking the canRead() and canWrite() methods on the current object. Notice the &ltJato:param> tag uses the Invoke Format just as the &ltJato:attribute> did previously.

To implement Jato functions, implement the org.jato.JatoFunction interface. The interface is defined as:

public interface JatoFunction {
   public String getName();
   public Object invoke(Properties parms, Interpreter jato, Object thisObj,
                        Element xmlIn, Element xmlOut)
   throws JatoException;
}

The getName() method returns the name of the function as it will be referenced in the script. The invoke() method contains the function implementation. It receives a reference to the current Jato interpreter, the current object, the current input XML element, and the current output XML element. Listing 4 shows the implementation of the file-perms() Jato function. Notice the parameters are passed to the function in a Properties object that allows the parameters to be accessed by name. The invoke() method returns the new value as a String. In this case, the parameters are passed as java.lang.Boolean objects even though canRead() and canWrite() return boolean (lowercase 'b') values. Primitive values will always be encapsulated in a java.lang wrapper class, such Character, Integer, Long, Double, or Float:

Listing 4: Jato function to format file permissions attribute

public class PermFunction implements JatoFunction {
   public String getName() { return "file-perms"; }
   public Object invoke(Properties parms, Interpreter jato, Object thisObj,
                        Element xmlIn, Element xmlOut)
   throws JatoException
   {
      Boolean rperm = (Boolean)parms.get("read");
      Boolean wperm = (Boolean)parms.get("write");
      return "" + (rperm.booleanValue() ?"r" :"") +
                  (wperm.booleanValue() ?"w" :"");
   }
}

Custom Jato functions must be registered before they may be referenced in a script. This is easily accomplished using the InterperterUtil.addFunction(JatoFunction f) method:

public static void main(String args[]) throws Exception {
   //load the built in tags, formats, and functions
   InterpreterUtil.loadDefaultJatoDefs();
   //register custom functions
   InterpreterUtil.addFunction(new PermFunction());
   . . .
}

Use your application's main() method as a convenient place for this registration, as it guarantees just a single occurrence.

Format XML content values

While functions enable developers to easily integrate complex Java functionality into a Jato script, formats allow for complex formatting of XML attributes, text, and other content. Jato includes built-in formats, but you can create custom formats by implementing the org.jato.JatoFormat interface, which resembles the JatoFormat interface. Before being referenced in a script, custom formats must be registered using the InterpreterUtil.addJatoFormat(JatoFormat f) method.

The requirements of our Jato script call for outputting dates with the format:

Day of week,  Month Day,  Year

An example date would be: "Sat, Mar 17, '01." Outputting dates in this fashion requires a format because Java provides the last file-modification date through the File class's lastModified() method, which returns the modified date as the number of milliseconds since January 1, 1970. Specifying dates in terms of milliseconds proves handy for calculations but it's lousy for human readability. In this case, we want to convert the value returned by lastModified() into a human-readable form. Formatting an attribute or text value simply consists of adding a 'format' attribute to a Jato tag:

&ltfile>
<Jato:attribute name='modified' invoke='lastModified' 
                   format="date(EEE, MMM d, ''yy)"/>
</file>

This attribute tag uses the built-in date format and outputs:

<file modified="Sat, Mar 17, '01"/>

The date format uses a java.text.SimpleDateFormat instance to perform formatting. If the format is specified as format="date()", then dates will be formatted using the SimpleDateFormat default format. Otherwise, simply specify a format pattern as documented in the SimpleDateFormat class documentation.

Our completed script is shown in Listing 5:

Listing 5: java-to-xml.xml Jato script

<Jato:defs xmlns:Jato='http://jato.sourceforge.net'>
   <Jato:translate key='root'>
      <Jato:attribute name='path' property='absolutePath'/>
      <Jato:inline macro='dir'/>
   </Jato:translate>
   <Jato:macros>
   <Jato:macro name='dir'>
      <dir>
         <Jato:attribute name='name' property='name'/>
         <Jato:inline macro='info'/>
         <Jato:debug print-elt='true'>Transforming directory</Jato:debug>
         <Jato:invoke method='listFiles'>
            <Jato:if property='directory'>
               <Jato:inline macro='dir'/>
               <Jato:else>
                  <Jato:debug print-object='true'>Transforming file</Jato:debug>
                  <file>
                     <Jato:text property='name'/>
                     <Jato:attribute name='size' invoke='length'/>
                     <Jato:inline macro='info'/>
                  </file>
               </Jato:else>
            </Jato:if>
         </Jato:invoke>
      </dir>
   </Jato:macro>
   <Jato:macro name='info'>
      <Jato:attribute name='modified' invoke='lastModified' format='date'/>
      <Jato:attribute name='permissions' function='file-perms'>
         <Jato:param name='read' invoke='canRead'/>
         <Jato:param name='write' invoke='canWrite'/>
      </Jato:attribute>
   </Jato:macro>
   </Jato:macros>
</Jato:defs>

The associated helper class is shown in Listing 6:

Listing 6: Helper class for java-to-xml.xml Jato script

import org.jdom.*;
import org.jdom.output.*;
import org.jdom.input.*;
import org.jato.*;
import java.io.*;
import java.util.*;
public class FileToXML extends XMLHelperAdapter {
   public FileToXml(File jatoFile) 
   throws JDOMException, IOException, JatoException 
   {
      super(jatoFile);
   }
   public Object getObject(String key) {
      if (key.equalsIgnoreCase("root")) {
         return new File(System.getProperty("user.dir")).getParentFile();
      } else {
         throw new IllegalArgumentException("Key not supported: " + key);
      }
   } 
   public static void main(String args[]) throws Exception {
      //load default tags and formats and add custom functions
      InterpreterUtil.loadDefaultJatoDefs();
      InterpreterUtil.addFunction(new PermFunction());
      JavaToXml jtox = new JavaToXml();
      jtox.setHelperClass("FileToXml");
      jtox.setSaveOption(true, "site.xml");
      org.jdom.Document doc = jtox.transform();
   } 
}

To run this script, simply type:

> java FileToXml

To run the code in the debugger as shown earlier, change the main() as follows:

public static void main(String args[]) throws Exception {
   InterpreterUtil.loadDefaultJatoDefs();
   //register custom functions
   InterpreterUtil.addFunction(new PermFunction());
   //have to turn debug generation on
   InterpreterUtil.setDebugEnabled(true);
   Interpreter jato = InterpreterUtil.createInterpreter(new FileToXml());
   //create and launch the debugger
   JatoDebugger debug = new JatoDebugger(jato);
   jato.attachDebugger(debug);
   Document doc = debug.transform();
   //save the file
   FileOutputStream xmlOS = new FileOutputStream("site.xml");
   new XMLOutputter("   ", true).output(doc, xmlOS);
   xmlOS.close();
}

Bringing up the debugger requires several calls. Before creating the interpreter, you must enable debugging by invoking the InterpreterUtil.setDebugEnabled(true). After creating the Jato interpreter, create a debugger instance and attach it to the interpreter. Instead of invoking the interpreter's transform() method directly, invoke the debugger's transform() method. If you wish to debug your Java code interfaced by Jato, start this program in your favorite Java debugger. You can then set breakpoints in the Java debugger and the Jato debugger and step through the execution of the script and the Java code. Wow, hours of free entertainment!

Create elements using <Jato:element>

So far, all scripts have created elements by placing regular XML tags in the script. Sometimes, however, there is a need to create an element programmatically. XML elements can be explicitly created using the <Jato:element> tag. The <dir> tag in our script could be created as follows:

<Jato:element name='dir' invoke='listFiles' recurse='true'>

This tag instructs Jato to:

  1. invoke='listFiles': Invoke the listFiles() method
  2. name='dir': Create a <dir> element for each object returned by the listFiles() method
  3. recurse='true': Perform the same command on each object returned by the listFiles() method

Of course, listFiles() returns an array of files that includes directories and files, so this command would create <dir> tags for files and directories. Considering that, we need some way to filter the objects returned by listFiles() so that it returns directories only.

Pass parameters to Java methods using <Jato:param>

The File class contains a means to filter the files returned by the listFiles() method by passing a java.io.FileFilter instance to the method. This requires that Jato pass an implementation of the FileFilter interface to the method. To pass parameters to methods, we employ the <Jato:param> tag. However, before looking at passing parameters, let's implement our filter class.

The File class contains a version of listFiles() with the following signature:

File[] listFiles(java.io.FilenameFilter filter)

In this case, FilenameFilter is a single method interface:

public interface FilenameFilter {
   public boolean accept(File dir, String name);
}

In our situation, we would implement FilenameFilter to filter all nondirectory files:

public class DirFilter implements FilenameFilter {
   public boolean accept(File dir, String name) {
      return new File(dir, name).isDirectory();
   }
}

How does it work? If writing straight Java, the code to obtain a list of directories contained in the current directory would be:

File curr = new File(".");         //get current directory
DirFilter df = new DirFilter();    //creat a filter
File list[] = curr.listFiles(df);  //get list of directories

Returning to our Jato example, we need to create an instance of DirFilter and pass it to listFiles() when creating <dir> elements. The new 'dir' macro is shown in Listing 7:

Listing 7: 'dir' macro that uses <Jato:element> to create XML <dir> tags

1. <Jato:macro name='dir'>
2.    <Jato:element name='dir' invoke='listFiles' recurse='true'>
3.       <Jato:param type='DirFilter'/>  
4.       <Jato:attribute name='name' property='name'/>
5.       <Jato:inline macro='files'/>
6.       <Jato:inline macro='info'/>
7.    </Jato:element>
8. </Jato:macro>

In Listing 7, line 2 recursively creates a <dir> element for each File object returned by listFiles(). Jato will automatically look for any enclosed <Jato:param> tags when invoking a Java method. At line 3, this script contains a single <Jato:param> tag, along with type='DirFilter', that instructs Jato to create a DirFilter instance. The keyword 'this', in lieu of a class name, specifies the current Jato object. The param tag also supports obtaining objects from the helper class using the 'get' attribute, such as get='dir-filter'. Notice the parameter tag does contain a name attribute. Parameter names are not utilized when invoking Java methods but are utilized in all other cases. You can specify a name attribute but it will be ignored.

In Listing 8, you'll see our finished script after writing a FileFilter class that filters out all directory classes and adding a <Jato:element> tag to generate <file> tags:

Listing 8: New script that uses <Jato:element> to create XML tags

<Jato:defs xmlns:Jato='http://jato.sourceforge.net'>
   <Jato:import>java.io.FilenameFilter</Jato:import>
   <Jato:translate key='root'>
      <Jato:attribute name='path' property='absolutePath'/>
      <Jato:inline macro='info'/>
      <Jato:element name='dir' invoke='listFiles' recurse='true'>
         <Jato:param type='DirFilter'/>
         <Jato:attribute name='name' property='name'/>
         <Jato:inline macro='info'/>
         <Jato:inline macro='files'/>
      </Jato:element>
      <Jato:inline macro='files'/>
   </Jato:translate>
   <Jato:macros>
   <Jato:macro name='files'>
      <Jato:element name='file' invoke='listFiles' recurse='true'>
         <Jato:param type='FileFilter'/>
         <Jato:text property='name'/>
         <Jato:attribute name='size' invoke='length'/>
         <Jato:inline macro='info'/>
      </Jato:element>
   </Jato:macro>
   <Jato:macro name='info'>
      <Jato:attribute name='modified' invoke='lastModified' format='date'/>
      <Jato:attribute name='permissions' function='file-perms'>
         <Jato:param name='read' invoke='canRead'/>
         <Jato:param name='write' invoke='canWrite'/>
      </Jato:attribute>
   </Jato:macro>
   </Jato:macros>
</Jato:defs>

The key differences between this script and the script in Listing 5 are:

  • The new script does not have any recursive macro invocations, allowing the 'dir' macro functionality to be inlined in the top-level script.
  • The new script does not include any conditional Jato expressions.
  • A new macro called 'file' creates <file> tags using the <Jato:element> tag and the newly written FileFilter class. Although this example utilized FileFilter and DirFilter default constructors, Jato supports instantiating objects using complex, nondefault constructors.

Personally, I prefer the second version based on its declarative form; however, its structure varies markedly from Java programs that accomplish a similar task. Indeed, many developers find developing Jato scripts more intuitive when they follow coding patterns similar to the patterns utilized when they write their Java programs.

Create elements in a namespace

As many XML documents utilize namespaces, Jato would be incomplete without such support. As such, Jato automatically supports namespaces for tags placed in a Jato script. To place the <dir> tag in the 'ls' namespace, the 'dir' macro in Listing 5 could be written as:

Listing 9: Explicitly create XML tags in a namespace

<Jato:macro name='dir' xmlns:ls='http://jato.sourceforge.net/ex/files'>
   <ls:dir> 
      <Jato:attribute name='name' property='name'/>
      <Jato:inline macro='info'/>
      <Jato:invoke method='listFiles'>
         <Jato:if property='directory'>
            <Jato:inline macro='dir'/>
            <Jato:else>
               <ls:file>
                  <Jato:text property='name'/>
                  <Jato:attribute name='size' invoke='length'/>
                  <Jato:inline macro='info'/>
               </ls:file>
            </Jato:else>
         </Jato:if>
      </Jato:invoke>
   </ls:dir>  
</Jato:macro>

Of course, as detailed in the XML specification, the 'xmlns' attribute can be specified any place within the XML document.

Placing <Jato:element>-created tags into a namespace requires an additional step: we must specify a <Jato:namespace> tag in the top level of the Jato script. This tag possesses attributes that specify the name and URI of the namespace. When specifying the name of the element to create in a <Jato:element> tag, specify the fully qualified name for the tag. Jato will parse the tag name, look up the namespace, and create the element within the specified namespace. Listing 10 shows how Listing 8 could be modified to create <ls:dir> tags:

Listing 10: Create XML tags in a namespace using <Jato:element> script tag

<Jato:defs xmlns:Jato='http://jato.sourceforge.net'>
   <Jato:import>java.io.FilenameFilter</Jato:import>
  <Jato:namespace name='ls' uri='http://jato.sourceforge.net/ex/files'/>  
   <Jato:translate key='root'>
      <Jato:attribute name='path' property='absolutePath'/>
      <Jato:inline macro='info'/>
      <Jato:element name='ls:dir' invoke='listFiles' recurse='true'>
         <Jato:param object='DirFilter' param-type='FilenameFilter'/>
         <Jato:attribute name='name' property='name'/>
         <Jato:inline macro='files'/>
         <Jato:inline macro='info'/>
      </Jato:element>
      <Jato:inline macro='files'/>
   </Jato:translate>
   . . .

Notice the <Jato:namespace> tag placed as a child of the <Jato:defs> tag. Although the ordering of the top-level tags is unimportant, the <Jato:import>, <Jato:namespace>, and <Jato:macros> tags must be placed as immediate descendants of the <Jato:defs> tag.

Conclusion

In this article, we looked in-depth at how to perform Java-to-XML transformations with the Jato API. Jato supports transformations that require complex attribute and text formatting, conditional tag generation, namespace support, debug statements, and recursive macro invocations. As with most languages, Jato supports multiple techniques for achieving the final programming task. Indeed, we explored using recursive macro calls and Jato's declarative syntax to generate the same XML document.

If you find Jato helpful for your projects, locate a bug, or think Jato could use a new feature, drop by the Jato project site and give us your input.

Learn more about this topic