Example using the Castor XML code generator
Documentation Author(s):
Werner Guttmann
API Reference:
The XML code generator API
Introduction The schema file Running the XMl code generator The generated code The Item.java class The PriceType.java class The Invoice.java class
Introduction
In this section we illustrate the use of the Source
Generator by explaining the generated classes from a
given XML schema. The XML code generator is going to be used
with the “java class mapping” property set to element
(default value).
The schema file
The input file is the schema file given with the XML code generator
example in the distribution of Castor
(under /src/examples/SourceGenerator/invoice.xsd).
<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://castor.exolab.org/Test/Invoice">
<xsd:annotation>
<xsd:documentation>
This is a test XML Schema for Castor XML.
</xsd:documentation>
</xsd:annotation>
<!--
A simple representation of an invoice. This is simply an example
and not meant to be an exact or even complete representation of an invoice.
-->
<xsd:element name="invoice">
<xsd:annotation>
<xsd:documentation>
A simple representation of an invoice
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ship-to">
<xsd:complexType>
<xsd:group ref="customer" />
</xsd:complexType>
</xsd:element>
<xsd:element ref="item"
maxOccurs="unbounded" minOccurs="1" />
<xsd:element ref="shipping-method" />
<xsd:element ref="shipping-date" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<!-- Description of a customer -->
<xsd:group name="customer">
<xsd:sequence>
<xsd:element name="name" type="xsd:string" />
<xsd:element ref="address" />
<xsd:element name="phone"
type="TelephoneNumberType" />
</xsd:sequence>
</xsd:group>
<!-- Description of an item -->
<xsd:element name="item">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Quantity"
type="xsd:integer" minOccurs="1" maxOccurs="1" />
<xsd:element name="Price" type="PriceType"
minOccurs="1" maxOccurs="1" />
</xsd:sequence>
<xsd:attributeGroup ref="ItemAttributes" />
</xsd:complexType>
</xsd:element>
<!-- Shipping Method -->
<xsd:element name="shipping-method">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="carrier"
type="xsd:string" />
<xsd:element name="option"
type="xsd:string" />
<xsd:element name="estimated-delivery"
type="xsd:duration" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<!-- Shipping date -->
<xsd:element name="shipping-date">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="date" type="xsd:date" />
<xsd:element name="time" type="xsd:time" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<!-- A simple U.S. based Address structure -->
<xsd:element name="address">
<xsd:annotation>
<xsd:documentation>
Represents a U.S. Address
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<!-- street address 1 -->
<xsd:element name="street1"
type="xsd:string" />
<!-- optional street address 2 -->
<xsd:element name="street2"
type="xsd:string" minOccurs="0" />
<!-- city-->
<xsd:element name="city" type="xsd:string" />
<!-- state code -->
<xsd:element name="state"
type="stateCodeType" />
<!-- zip-code -->
<xsd:element ref="zip-code" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<!-- A U.S. Zip Code -->
<xsd:element name="zip-code">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:pattern value="[0-9]{5}(-[0-9]{4})?" />
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<!-- State Code
obviously not a valid state code....but this is just
an example and I don't feel like creating all the valid
ones.
-->
<xsd:simpleType name="stateCodeType">
<xsd:restriction base="xsd:string">
<xsd:pattern value="[A-Z]{2}" />
</xsd:restriction>
</xsd:simpleType>
<!-- Telephone Number -->
<xsd:simpleType name="TelephoneNumberType">
<xsd:restriction base="xsd:string">
<xsd:length value="12" />
<xsd:pattern value="[0-9]{3}-[0-9]{3}-[0-9]{4}" />
</xsd:restriction>
</xsd:simpleType>
<!-- Cool price type -->
<xsd:simpleType name="PriceType">
<xsd:restriction base="xsd:decimal">
<xsd:fractionDigits value="2" />
<xsd:totalDigits value="5" />
<xsd:minInclusive value="1" />
<xsd:maxInclusive value="100" />
</xsd:restriction>
</xsd:simpleType>
<!-- The attributes for an Item -->
<xsd:attributeGroup name="ItemAttributes">
<xsd:attribute name="Id" type="xsd:ID" minOccurs="1"
maxOccurs="1" />
<xsd:attribute name="InStock" type="xsd:boolean"
default="false" />
<xsd:attribute name="Category" type="xsd:string"
use="required" />
</xsd:attributeGroup>
</xsd:schema> |
|
The structure of this schema is simple: it is composed of a
top-level element which is a complexType with references to
other elements inside. This schema represents a simple
invoice: an invoice is a customer (customer top-level
group), an article (item element), a shipping method
(shipping-method element) and a shipping date
(shipping-date element). Notice that the ship-to element
uses a reference to an address element. This address
element is a top-level element that contains a reference to
a non-top-level element (the zip-cod element). At the end
of the schema we have two simpleTypes for representing a
telephone number and a price. The Source Generator is used
with the element property set for class creation
so a class is going to be generated for all top-level elements. No classes
are going to be generated for complexTypes and simpleTypes since the
simpleType is not an enumeration.
To summarize, we can expect 7 classes : Invoice, Customer,
Address, Item, ShipTo, ShippingMethod
and ShippingDate and the 7 corresponding class descriptors. Note
that a class is generated for the top-level group customer
Running the XMl code generator
To run the source generator and create the source from the
invoice.xsd file in a package test, we just call
in the command line:
java -cp %CP% org.exolab.castor.builder.SourceGenerator -i invoice.xsd -package test
|
|
The generated code
The Item.java class
To simplify this example we now focus on the item element.
<!-- Description of an item -->
<xsd:element name="item">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Quantity" type="xsd:integer"
minOccurs="1" maxOccurs="1" />
<xsd:element name="Price" type="PriceType"
minOccurs="1" maxOccurs="1" />
</xsd:sequence>
<xsd:attributeGroup ref="ItemAttributes" />
</xsd:complexType>
</xsd:element>
<!-- Cool price type -->
<xsd:simpleType name="PriceType">
<xsd:restriction base="xsd:decimal">
<xsd:fractionDigits value="2" />
<xsd:totalDigits value="5" />
<xsd:minInclusive value="1" />
<xsd:maxInclusive value="100" />
</xsd:restriction>
</xsd:simpleType>
<!-- The attributes for an Item -->
<xsd:attributeGroup name="ItemAttributes">
<xsd:attribute name="Id" type="xsd:ID" minOccurs="1" maxOccurs="1" />
<xsd:attribute name="InStock" type="xsd:boolean" default="false" />
<xsd:attribute name="Category" type="xsd:string" use="required" />
</xsd:attributeGroup> |
|
To represent an Item object, we need to know its Id, the
Quantity ordered and the Price for one item. So we can
expect to find a least three private variables: a string for
the Id element, an int for the quantity element (see the
section on XML Schema support if you want to see the mapping
between a W3C XML Schema type and a java type), but what type
for the Price element?
While processing the Price
element, Castor is going to process the type of Price i.e.
the simpleType PriceType which base is decimal. Since
derived types are automatically mapped to parent types and
W3C XML Schema decimal type is mapped to a
java.math.BigDecimal, the price element will be a
java.math.BigDecimal. Another private variable is created
for quantity: quantity is mapped to a primitive java type,
so a boolean has_quantity is created for monitoring the
state of the quantity variable. The rest of the code is the
getter/setter methods and the Marshalling framework
specific methods. Please find below the complete Item class
(with Javadoc comments stripped off):
/**
* This class was automatically generated with
* Castor 1.0.4,
* using an XML Schema.
*/
package test;
public class Item implements java.io.Serializable {
//--------------------------/
//- Class/Member Variables -/
//--------------------------/
private java.lang.String _id;
private int _quantity;
/**
* keeps track of state for field: _quantity
*/
private boolean _has_quantity;
private java.math.BigDecimal _price;
//----------------/
//- Constructors -/
//----------------/
public Item() {
super();
} //-- test.Item()
//-----------/
//- Methods -/
//-----------/
public java.lang.String getId() {
return this._id; $
} //-- java.lang.String getId()
public java.math.BigDecimal getPrice() {
return this._price;
} //-- java.math.BigDecimal getPrice()
public int getQuantity() {
return this._quantity;
} //-- int getQuantity()
public boolean hasQuantity() {
return this._has_quantity;
} //-- boolean hasQuantity()
public boolean isValid() {
try {
validate();
} catch (org.exolab.castor.xml.ValidationException vex) {
return false;
}
return true;
} //-- boolean isValid()
public void marshal(java.io.Writer out)
throws org.exolab.castor.xml.MarshalException,org.exolab.castor.xml.ValidationException {
Marshaller.marshal(this, out);
} //-- void marshal(java.io.Writer)
public void marshal(org.xml.sax.DocumentHandler handler)
throws org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
Marshaller.marshal(this, handler);
} //-- void marshal(org.xml.sax.DocumentHandler)
public void setId(java.lang.String _id) {
this._id = _id;
} //-- void setId(java.lang.String)
public void setPrice(java.math.BigDecimal _price) {
this._price = _price;
} //-- void setPrice(java.math.BigDecimal)
public void setQuantity(int _quantity) {
this._quantity = _quantity;
this._has_quantity = true;
} //-- void setQuantity(int)
public static test.Item unmarshal(java.io.Reader reader)
throws org.exolab.castor.xml.MarshalException,org.exolab.castor.xml.ValidationException {
return (test.Item) Unmarshaller.unmarshal(test.Item.class, reader);
} //-- test.Item unmarshal(java.io.Reader)
public void validate()
throws org.exolab.castor.xml.ValidationException {
org.exolab.castor.xml.Validator.validate(this, null);
} //-- void validate()
}
|
|
The ItemDescriptor class is a bit more complex. This class
is containing inner classes which are the XML field
descriptors for the different components of an ‘Item’
element i.e. id, quantity and price.
The PriceType.java class
TODO ...
The Invoice.java class
In this section, we focus on the 'invoice' element as
shown again below:
<xsd:element name="invoice">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ship-to">
<xsd:complexType>
<xsd:group ref="customer" />
</xsd:complexType>
</xsd:element>
<xsd:element ref="item" minOccurs="1" maxOccurs="unbounded" />
<xsd:element ref="shipping-method" />
<xsd:element ref="shipping-date" />
</xsd:sequence>
</xsd:complexType>
</xsd:element> |
|
Amongst other things, an <invoice> is made up of at least
one, but potentially many <item> elements. The Castor XML code
generator creates a Java collection named 'itemList' for this
unbounded element declaration, of type java.util.List
if the scode generator is used with the 'arraylist'
field factory.
private java.util.List _itemList; |
|
If the 'j1' field factory is used, this will be replaced
with ...
private java.util.Vector _itemList; |
|
The complete class as generated (with irrelevant code
parts removed) in 'j2' (aka 'arraylist')
mode is shown below:
public class Invoice implements java.io.Serializable {
...
private java.util.List _itemList;
...
public Invoice()
{
super();
this._itemList = new java.util.ArrayList();
} //-- xml.c1677.invoice.generated.Invoice()
...
public void addItem(xml.c1677.invoice.generated.Item vItem)
throws java.lang.IndexOutOfBoundsException
{
this._itemList.add(vItem);
} //-- void addItem(xml.c1677.invoice.generated.Item)
public void addItem(int index, xml.c1677.invoice.generated.Item vItem)
throws java.lang.IndexOutOfBoundsException
{
this._itemList.add(index, vItem);
} //-- void addItem(int, xml.c1677.invoice.generated.Item)
public java.util.Enumeration enumerateItem()
{
return java.util.Collections.enumeration(this._itemList);
} //-- java.util.Enumeration enumerateItem()
public xml.c1677.invoice.generated.Item getItem(int index)
throws java.lang.IndexOutOfBoundsException
{
// check bounds for index
if (index < 0 || index >= this._itemList.size()) {
throw new IndexOutOfBoundsException("getItem: Index value '" + index
+ "' not in range [0.." + (this._itemList.size() - 1) + "]");
}
return (xml.c1677.invoice.generated.Item) _itemList.get(index);
} //-- xml.c1677.invoice.generated.Item getItem(int)
public xml.c1677.invoice.generated.Item[] getItem()
{
int size = this._itemList.size();
xml.c1677.invoice.generated.Item[] array = new xml.c1677.invoice.generated.Item[size];
for (int index = 0; index < size; index++){
array[index] = (xml.c1677.invoice.generated.Item) _itemList.get(index);
}
return array;
} //-- xml.c1677.invoice.generated.Item[] getItem()
public int getItemCount()
{
return this._itemList.size();
} //-- int getItemCount()
public java.util.Iterator iterateItem()
{
return this._itemList.iterator();
} //-- java.util.Iterator iterateItem()
public void removeAllItem()
{
this._itemList.clear();
} //-- void removeAllItem()
public boolean removeItem(xml.c1677.invoice.generated.Item vItem)
{
boolean removed = _itemList.remove(vItem);
return removed;
} //-- boolean removeItem(xml.c1677.invoice.generated.Item)
public xml.c1677.invoice.generated.Item removeItemAt(int index)
{
Object obj = this._itemList.remove(index);
return (xml.c1677.invoice.generated.Item) obj;
} //-- xml.c1677.invoice.generated.Item removeItemAt(int)
public void setItem(int index, xml.c1677.invoice.generated.Item vItem)
throws java.lang.IndexOutOfBoundsException
{
// check bounds for index
if (index < 0 || index >= this._itemList.size()) {
throw new IndexOutOfBoundsException("setItem: Index value '"
+ index + "' not in range [0.." + (this._itemList.size() - 1) + "]");
}
this._itemList.set(index, vItem);
} //-- void setItem(int, xml.c1677.invoice.generated.Item)
public void setItem(xml.c1677.invoice.generated.Item[] vItemArray)
{
//-- copy array
_itemList.clear();
for (int i = 0; i < vItemArray.length; i++) {
this._itemList.add(vItemArray[i]);
}
} //-- void setItem(xml.c1677.invoice.generated.Item)
} |
|
|