Next: XML Schema Built-in Types Up: XML Enhancements to Java™ Previous: Bibliography

Subsections


Example

We now illustrate the use of the XJ extensions of Java using a sample XJ program. We first list the XML Schema imported by the program and the data processed by the program. We then describe in detail each construct of the sample program. The sample program Totals.xj, the imported XML Schema, and the XML data file can be found in the distribution in the sub-directory samples/com/ibm/xj/samples/totals.

XML Schema - salesschema.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:element name="salesdata">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="year" type="YearType" minOccurs="0" maxOccurs="unbounded"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>

    <xsd:complexType name="YearType">
        <xsd:sequence>
            <xsd:element name="theyear" type="xsd:string"/>
            <xsd:element name="sales" type="SalesType"/>
            <xsd:element name="region" type="RegionType" minOccurs="0" maxOccurs="unbounded"/>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="SalesType">
        <xsd:simpleContent>
            <xsd:extension base="xsd:double">
                <xsd:attribute name="unit" type="xsd:string"/>
            </xsd:extension>
        </xsd:simpleContent>
    </xsd:complexType>
    <xsd:complexType name="RegionType">
        <xsd:sequence>
            <xsd:element name="name" type="xsd:string"/>
            <xsd:element name="sales" type="SalesType"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:schema>

XML Data File - chart.xml

<?xml version="1.0"?>
<salesdata xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="salesdata.xsd">
    <year>
        <theyear>1997</theyear>
        <region>
            <name>west</name>
            <sales unit="millions">32</sales>
        </region>
        <region>
            <name>central</name>
            <sales unit="millions">11</sales>
        </region>
        <region>
            <name>east</name>
            <sales unit="millions">19</sales>
        </region>
    </year>
    <year>
        <theyear>1998</theyear>
        <region>
            <name>west</name>
            <sales unit="millions">35</sales>
        </region>
        <region>
            <name>central</name>
            <sales unit="millions">12</sales>
        </region>
        <region>
            <name>east</name>
            <sales unit="millions">25</sales>
        </region>
    </year>
    <year>
        <theyear>1999</theyear>
        <region>
            <name>west</name>
            <sales unit="millions">36</sales>
        </region>
        <region>
            <name>central</name>
            <sales unit="millions">12</sales>
        </region>
        <region>
            <name>east</name>
            <sales unit="millions">31</sales>
        </region>
    </year>
    <year>
        <theyear>2000</theyear>
        <region>
            <name>west</name>
            <sales unit="millions">37</sales>
        </region>
        <region>
            <name>central</name>
            <sales unit="millions">11</sales>
        </region>
        <region>
            <name>east</name>
            <sales unit="millions">40</sales>
        </region>
    </year>
</salesdata>


Program Description

Totals.xj performs a simple data processing job. It works on a fairly familiar schema, describing sales per each year of interest, and within each year, per each region of interest. The document element is salesdata. It contains a sequence of year elements. A year element contains the year (element theyear). In addition, it contains a sequence of region elements. A region element, in turn, contains the name of the region (element name) and the sales in this region (element sales).

package com.ibm.xj.samples.totals;

import java.io.FileInputStream;

import com.ibm.xj.io.XMLDocumentOutputStream;
import com.ibm.xj.samples.driver.Benchmark;

import com.ibm.xj.samples.totals.salesschema.*;

public class Totals implements Benchmark {
    private salesdata document;

    public static void main(String[] argv) {
        if (argv.length != 1) {
            System.err.println("Usage: Totals <filename>");
            System.exit(-1);
        }
        Totals t = new Totals();
        t.doParse(argv[0]);
        t.doExecute();
    }

    public void doParse(String filename) {
        try {
            document = new salesdata(new FileInputStream(filename));
        }
        catch (java.io.IOException e) {
            throw new Error("Cannot parse input file");
        }
    }

    public void doExecute() {
        computeSales(document);
    }

    private static void computeSales(salesdata sd) {
        int min = 70;
        System.out.println("Total Sales");
        double grandTotal = 0;
        Sequence<year> ys = sd[|year[sum(.//sales) > $min]|];
        if (ys.isEmpty())
            System.out.println("No elements matched");

        XMLCursor<year> y = ys.iterator();
        while (y.hasNext()) {
            year ytmp = y.next();
            String str = ytmp[|theyear|];
            System.out.print(str + "\t");
            double total = 0;
            XMLCursor<sales> s = ytmp[|.//sales|].iterator();
            while (s.hasNext()) {
                sales stmp =  s.next();
                total = total + stmp;
            }
            grandTotal += total;
            System.out.println(total);
        }
        double conversionFactor = 1.8;
        sales s2 = new sales(<sales unit="GBP">{conversionFactor * grandTotal}</sales>);

        XMLDocumentOutputStream out = new XMLDocumentOutputStream(System.out);
        out.println(s2);

        System.out.println("\n Grand Total: " + grandTotal);
    }
}

We now examine this program, statement by statement, and explain the various XJ features that are employed.

The preamble:

package com.ibm.xj.samples.totals;

The above specifies the package in which the Java class resides.

import java.io.FileInputStream;

import com.ibm.xj.io.XMLDocumentOutputStream;
import com.ibm.xj.samples.driver.Benchmark;

As an XJ program is a Java program, one can import any Java classes and interfaces.

import com.ibm.xj.samples.totals.salesschema.*;

This statement notifies the XJ compiler that the XML schema entities (elements and their types) in the file "salesschema.xsd" in package com.ibm.xj.samples.totals should be imported into the program.

public class Totals implements Benchmark {
    private salesdata document;

The file defines a Java class -- Totals that implements a Java interface Benchmark[*]. Within this class, the first declaration is of a private variable named document. Its type is salesdata, which is an XML type. The XJ compiler will find the definition of salesdata in the imported schema file.

    public static void main(String[] argv) {
        if (argv.length != 1) {
            System.err.println("Usage: Totals <filename>");
            System.exit(-1);
        }
        Totals t = new Totals();
        t.doParse(argv[0]);
        t.doExecute();
    }

This main method creates an object of class Totals and asks it to perform two tasks:

Incidentally, the two methods above are defined in the Benchmark interface.

    public void doParse(String filename) {
        try {
            document = new salesdata(new FileInputStream(filename));
        }
        catch (java.io.IOException e) {
            throw new Error("Cannot parse input file");
        }
    }

Consider the statement document = new salesdata(new FileInputStream(filename));

It performs loading, parsing and validation[*] of the data in file filename and associates an XML value with the XML variable document.

    public void doExecute() {
        computeSales(document);
    }

Method doExecute performs the data processing task by invoking the computeSales static method on the document field.

    private static void computeSales(salesdata sd) {
        int min = 70;
        System.out.println("Total Sales");
        double grandTotal = 0;

Method computeSales has a single argument, an XML parameter sd of type salesdata. It first prints the output heading and initializes a double value grandTotal to 0. Then, it performs:

        Sequence<year> ys = sd[|year[sum(.//sales) > $min]|];

This statement defines an XJ sequence, ys, parameterized[*] by the XML type year. This sequence will contain, at run-time, references to XML values. It is obtained by applying an XPath expression to the XML value referenced by variable sd (the argument to computeSales). The XPath expression is sd[|/year[sum(.//sales) > $min]|], which finds all years for which the sum of the values contained in the sales elements is greater than the value of min. Note that this XPath expression refers to the XJ variable min. At runtime, the value of min would be substituted before the evaluation of the XPath expression. Formally, this expression results in a sequence of XML items (all are nodes in this case). XJ's implementation is to populate the sequence with references to XML elements or instances of atomic classes as appropriate.

        if (ys.isEmpty())
            System.out.println("No elements matched");

This statement checks if the above sequence is empty, and prints a message if so. The isEmpty() method is defined on all Sequences.

        XMLCursor<year> y = ys.iterator();

Here, an XMLCursor is created for the ys sequence. An XMLCursor is very similar to the Java Iterator, but is generic.

        while (y.hasNext()) {
            year ytmp = y.next();

The while loop iterates over the elements of y. These elements are references to year elements. Variable ytmp references the current year element.

            String str = ytmp[|theyear|];
            System.out.print(str + "\t");
            double total = 0;

String str is assigned the character content of theyear sub-element of element year. The variable total, the sum for this year, is initialized to zero. At this point the sales of the current year element are to be added.

            XMLCursor<sales> s = ytmp[|.//sales|].iterator();

The XMLCursor s will iterate over the sales elements within the current year element.

            while (s.hasNext()) {
                sales stmp = s.next();

The loop iterates through the sales elements in s. Variable stmp is the current sales element.

                total = total + stmp;

The total for this year element is updated. Note that there's an implicit coercion from the XML element sales that has contents of type xsd:double to double.

            }
            grandTotal += total;
            System.out.println(total);

The grandTotal (over all years) is incremented; the total for this year is printed.

The following demonstrates the XML value construction facilities of XJ:

        }
        double conversionFactor = 1.8;
        sales s2 = new sales(<sales unit="GBP">{conversionFactor * grandTotal}</sales>);

It creates a new XML value using the literal XML constructor and stores in the variable s2. The braces "{}" enclose an XJ expression which is evaluated at runtime to generate a value that is embedded in the constructed XML.

Variable s2 references the newly constructed XML value. The statement illustrates one way in which data can be assigned to this element. The XML value of s2 at runtime is:

<?xml version="1.0" encoding="UTF-8"?>
<sales unit="GBP">430.2</sales>

Next,

        XMLDocumentOutputStream out = new XMLDocumentOutputStream(System.out);

A new com.ibm.xj.io.XMLDocumentOutputStream object is created, which adds XML output capability to the java.io.PrintStream class. Then,

        out.println(s2);

s2 is serialized into plain XML and is printed to standard output via the XMLDocumentOutputStream class.

        System.out.println("\n Grand Total: " + grandTotal);
    }
}

Finally, " Grand Total: 239.0" is printed.

Totals: A complete run

The compilation and execution are as follows:

[$XJ_HOME/samples]$ export JAVA_HOME="c:/Program Files/IBM/Java14"
[$XJ_HOME/samples]$ ../bin/xjc com/ibm/xj/samples/totals/Totals.xj

[$XJ_HOME/samples]$ ../bin/xj com.ibm.xj.samples.totals.Totals com/ibm/xj/samples/totals/chart.xml

Total Sales
1998    72.0
1999    79.0
2000    88.0
<?xml version="1.0" encoding="UTF-8"?>
<sales unit="GBP">430.2</sales>

Grand Total: 239.0

[$XJ_HOME/samples]$



Footnotes

...Benchmark[*]
A utility interface for micro-benchmarking
... validation[*]
Validation of the document is currently disabled by default.
... parameterized[*]
Java 1.5 style generics are only supported for XML types at the moment.


Next: XML Schema Built-in Types Up: XML Enhancements to Java™ Previous: Bibliography
XJ Group 2005-09-13