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:
- Part 1: A new library for converting between Java and XML
- Part 2: Look in-depth at Java-to-XML translation
- Part 3: Translate XML documents into Java objects
In Part 3, we turn our attention to XML-to-Java transformations and the conditional tags
<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.
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 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: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.Systemas 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
Systemclass 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
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
JTable. The component uses a
TreeTableModel as the data model; we will employ a concrete implementation called 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));
Notice that all
JTreeTable classes reside in the tree package. The code fragment in Listing 2 uses two concrete implementations of the
DirNode: Represents a directory that can contain other directory and file nodes. The
DirNodeprovides 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.
ContentNodedefines 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
ContentNodeclass to set the byte size. The
DirNodeclass 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
Putting it all together, our script needs to perform the following:
- Create a root
- Iterate each element in
- Instantiate a
DirNodeinstance for each
<dir>tag and add it to the parent
- Instantiate a
ContentNodeinstance for each
<file>tag and add it to the parent
- Create a
Dateobject using the value specified in the modified attribute and invoke the node's
- For each
<file>element, we also need to invoke the
setSize()method passing size attribute's value
- Instantiate a
- Create a
FileSystemModelusing 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
Dateclass and all the
JTableTreeclasses in the tree package.
- Line 5: The
<Jato:object>tag creates Java objects. In this case, the type attribute specifies the
DirNodeclass 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
- Line 6: Invokes the
'dir'macro, which will process the root's
- 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
JTreeTableto display the directory nodes before the file nodes.
- Line 11: Declares the start of the
- 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
- Line 22: Searches the current input XML element for
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
'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: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: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.
'dir' macro requires three tags: