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

Translate XML documents into Java objects

1 2 3 Page 2
Page 2 of 3
  • <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 2 3 Page 2
Page 2 of 3