Tuesday, June 17, 2008

Code Generator Part VIII: The generated code

As the concluding part of the Code Generator series here is an example of how the generator works to generate the code. In this post I am going to present a sample object definition as the input (order.xml) to generate couple of Java source files using the home made code generator.

A piece of code generated using your home made Code Generator

Here is an example of an XML object specification for Customer, Order, OrderItem and Item objects to be supplied as the input for the code generation.


Order.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- order.xml -->
<OOSpec name="Order">
<Package name="">
<Class name="Customer" stereotype="class" visibility="public" baseInterfaces="Transformer">
<Import reference="java.util.Vector"/>
<Attribute name="code"/>
<Attribute name="names" multiplicity="3" defaultValue="Partha,Sarathi,Sengupta"/>
<Attribute name="age" type="integer"/>
<Attribute name="contacts" multiplicity="*"/>
<Operation constructor="yes" documentation="This is default constructor"></Operation>
<Operation directive="" documentation="This is transform operation" name="transform" returnType="string" bodyClassName="com. codegenerator.ToXMLStringBody">
</Operation>
</Class>
<Class name="Order" baseInterfaces="Transformer">
<Attribute name="number"/>
<Attribute name="date" type="date"/>
<Operation constructor="yes"></Operation>
<Operation name="transform" returnType="string" bodyClassName="com. codegenerator.ToXMLStringBody" synchronized="yes">
</Operation>
</Class>
<Class name="OrderItem" baseInterfaces="Transformer">
<Attribute name="number" type="integer"/>
<Attribute name="quantity" type="integer"/>
<Operation constructor="yes"></Operation>
<Operation name="transform" returnType="string" bodyClassName="com. codegenerator.ToXMLStringBody">
</Operation>
</Class>
<Class name="Item" documentation="This is a sample documentation">
<Attribute name="number"/>
<Attribute name="description" defaultValue="This is a description of the item"/>
<Attribute name="price" type="double" defaultValue="2.12"/>
<Operation constructor="yes"></Operation>
<Operation name="transform" returnType="string" visibility="public" bodyClassName="com.codegenerator.ToXMLStringBody">
</Operation>
</Class>
<Class name="Transformer" stereotype="interface" baseInterfaces="java.io.Serializable">
<Operation name="transform" returnType="string"></Operation>
</Class>
</Package>
<Association name="">
<Role class="Customer" multiplicity="1" name="" start="yes" navigable="no"/>
<Role class="Order" multiplicity="2" name="myOrders" navigable="yes"/>
</Association>
<Association name="">
<Role class="Order" multiplicity="1" name="order" start="yes" navigable="no"/>
<Role class="OrderItem" multiplicity="4" name="orderItems" navigable="yes"/>
</Association>
<Association name="">
<Role class="OrderItem" multiplicity="1" name="" start="yes" navigable="no"/>
<Role class="Item" multiplicity="1" name="item" navigable="yes"/>
</Association>
</OOSpec>


The Java source codes generated from the above specification are presented below. Notice how the transform method has been generated differently in each class.
Customer.java

/* This file has been generated by the CodeGenerator */
package com.order;
import java.util.Vector;

public class Customer implements Transformer {
private String code;
private String[] names;
private int age;
private java.util.ArrayList contacts;
private Order[] myOrders;

public String getCode() { return this.code; }
public void setCode(String code) { this.code = code; }
public String[] getNames() { return this.names; }
public void setNames(String[] names) { this.names = names; }
public int getAge() { return this.age; }
public void setAge(int age) {this.age = age;}
public java.util.ArrayList getContacts() {return this.contacts;}
public String getContacts(int index) {return (String)this.contacts.get(index);}
public void setContacts(java.util.ArrayList contacts) {this.contacts = contacts;}
public void addContacts(String contacts) {this.contacts.add(contacts);}
public Order[] getMyOrders() {return this.myOrders;}
public void setMyOrders(Order[] myOrders) { this.myOrders = myOrders;}
public Customer() {
code = new String();
names = new String[3];
names[0] = new String("Partha");
names[1] = new String("Sarathi");
names[2] = new String("Sengupta");
age = 0;
contacts = new java.util.ArrayList();
myOrders = new Order[2];
for(int i=0; i<myOrders.length; i++) { myOrders[i] = new Order(); }
}

/*This is transform operation*/
public String transform() {
StringBuffer sb = new StringBuffer();
if(code != null && !code.toString().equals("")) {
sb.append("<code>"+code+"</code>");
}
else {
sb.append("<code/>");
}
for(int i=0; i<names.length; i++) {
if(names[i] != null && !names[i].toString().equals("")) {
sb.append("<names>"+names[i]+"</names>");
}
Else {
sb.append("<names/>");
}
}
sb.append("<age>"+age+"</age>");

for(int i=0; i<contacts.size(); i++) {
if(contacts.get(i) != null && !contacts.get(i).toString().equals("")) {
sb.append("<contacts>"+((String)contacts.get(i)).toString()+"</contacts>");
}
Else {
sb.append("<contacts/>");
}
}
for(int i=0; i<myOrders.length; i++) {
if(myOrders[i] != null){
sb.append("<myOrders>");
sb.append(myOrders[i].transform());
sb.append("</myOrders>");
}
Else {
sb.append("<myOrders/>");
}
}
return sb.toString();
}
}
Order.java

/* This file has been generated by the CodeGenerator */

package com.order;
import java.util.*;

public class Order implements Transformer{
private String number;
private java.util.Date date;
private OrderItem[] orderItems;
public String getNumber() {return this.number;}
public void setNumber(String number) { this.number = number; }
public java.util.Date getDate() { return this.date; }
public void setDate(java.util.Date date) { this.date = date; }
public OrderItem[] getOrderItems() { return this.orderItems; }
public void setOrderItems(OrderItem[] orderItems) { this.orderItems = orderItems; }
public Order(){
number = new String();
date = new java.util.Date();
orderItems = new OrderItem[4];
for(int i=0; i<orderItems.length; i++) {
orderItems[i] = new OrderItem();
}
}
public synchronized String transform() {
StringBuffer sb = new StringBuffer();
if(number != null && !number.toString().equals("")){
sb.append("<number>"+number+"</number>");
}
else {
sb.append("<number/>");
}
if(date != null && !date.toString().equals("")) {
sb.append("<date>"+date+"</date>");
}
else {
sb.append("<date/>");
}
for(int i=0; i<orderItems.length; i++) {
if(orderItems[i] != null){
sb.append("<orderItems>");
sb.append(orderItems[i].transform());
sb.append("</orderItems>");
}
else {
sb.append("<orderItems/>");
}
}
return sb.toString();
}
}
Item.java

/* This file has been generated by the CodeGenerator */

package com.order;
import java.util.*;

/*This is a sample documentation*/
public class Item implements java.io.Serializable{
private String number;
private String description;
private double price;
public String getNumber() { return this.number; }
public void setNumber(String number) { this.number = number; }
public String getDescription() { return this.description; }
public void setDescription(String description) { this.description = description; }
public double getPrice() { return this.price; }
public void setPrice(double price) { this.price = price; }
public Item() {
number = new String();
description = new String("This is a description of the item");
price = 2.12;
}
public String transform(){
StringBuffer sb = new StringBuffer();
if(number != null && !number.toString().equals("")){
sb.append("<number>"+number+"</number>");
}
else{
sb.append("<number/>");
}
if(description != null && !description.toString().equals("")) {
sb.append("<description>"+description+"</description>");
}
else{
sb.append("<description/>");
}
sb.append("<price>"+price+"</price>");
return sb.toString();
}
}
OrderItem.java

/* This file has been generated by the CodeGenerator */

package com.order;
import java.util.*;

public class OrderItem implements Transformer{
private int number;
private int quantity;
private Item item;
public int getNumber(){ return this.number; }
public void setNumber(int number) { this.number = number; }
public int getQuantity(){ return this.quantity; }
public void setQuantity(int quantity){ this.quantity = quantity; }
public Item getItem(){ return this.item; }
public void setItem(Item item){ this.item = item; }
public OrderItem(){
number = 0;
quantity = 0;
item = new Item();
}
public String transform(){
StringBuffer sb = new StringBuffer();
sb.append("<number>"+number+"</number>");
sb.append("<quantity>"+quantity+"</quantity>");
if(item != null && !item.toString().equals("")) {
sb.append("<item>");
sb.append(item.transform());
sb.append("</item>");
}
else{
sb.append("<item/>");
}
return sb.toString();
}
}
Hope this article will help those who are looking for preliminary information on the concepts and technologies used in code generation. Please feel free to leave your comments.

Wednesday, June 4, 2008

Code Generator Part VII: Controller Layer

Finally here comes the control room for the code generation process. This is the place where the Importer, Exporter and the IOM get together to generate the code for me. This layer controls the orchestration of the entire process and is designed to be unaffected by any change in the Importer, Exporter or IOM layers. In my next post I will try to provide an example to explain how the code generator actually works in reality and generates code.

The Control Layer
It’s layer from where the Code Generator gets executed by the user. It has mainly two classes Generator and GeneratorUtility as shown in Fig 1.


Fig 1

This shows that the Generator class instantiates the Importer and Exporter type of classes. In case of Java source code generation, the Generator instantiates the XMLOOImporter (Fig 6) which implements the Importer interface for importing the XML input model. Once the XML model is imported and stored in the memory in the form of IOM model, the Generator instantiates the JavaExporter (Fig 7) which implements the Exporter interface for generating the Java source files as per the IOM model (Fig 3).

Generator is the common entry point for both code generation and template generation processes. This class has mainly two methods called “generate” and “main”.
The “generate” method takes 4 parameters as input – input model type (i.e. XML), output source code language (i.e. Java), name of the input model file (i.e. XML model file name) and the output destination directory. To generate the source codes, this class invokes the importer and the exporter simultaneously to read the class specifications from the XML input model file and to generate Java source code according to the class specifications.
Since this class also has a “main” method, it can be invoked from the command-line by using the 4 parameters described above. The Generator class is shown in figure 1.

GeneratorUtility is a common utility class that gets used by most of the classes involved in the code generator system. It contains some static values and utility methods used for intermediate processing or formatting. The GeneratorUtility class is shown in figure 1.
Below is a high-level sequence diagram of the Code Generator.



Fig 2

Actor invokes the Code Generator by calling the generate() method of the main class “Generator”. The actor specifies the input type (i.e. XML), output type (i.e. Java), input model file (XML file) name and the output destination (directory or path) while calling this method (Fig 2).
In case of Java source code generation, the generate() method instantiates XMLOOImporter class (which implements Importer) and invokes its start() method to read the class specifications from the XML model file provided as input (Fig 2). Once the classes are identified from the input model (XML), Generator instantiates the JavaExporter class (which implements Exporter interface) to generate java source files from those classes as specified in the XML model (Fig 2). At the end of generating the source files the Code Generator system will display a list of source files that have been generated and a message with the number of source files. Incase an invalid (not an XML) input document is provided to the system it will throw an exception and display the stack-trace for the exception.
Figure 3 shows detailed sequence diagram of the code generation process.


Fig 3
This section provides the relationship or mapping between the XML-tags used in the XML model supplied as the input to the Code Generator and the corresponding IOM class that represents the tag in the memory object model (IOM).
Figure 4 shows the relationship between different components defined in XML and realized in IOM for the source code generation process. Blue boxes marked with "XML Tag" in the diagram represents the tags used in the input XML model and other boxes are representing the IOM classes already shown in Part IV of this series. The attributes shown in the XML-tags (blue boxes) are the XML-attributes. The names of the XML-attributes are quite identical to those of the corresponding IOM class e.g. the attributes – name, stereotype, visibility, documentation, isStatic, isFinal, isAbstract etc. of IOMClass are having identical names with the respective XML-attributes (i.e. name, stereotype, visibility, documentation, static, final, abstract) in the Class tag. The attributes that are not in the XML-tags but present in corresponding IOM class are used for internal processing of Code Generator.
Since XML does not allow data types other than character strings, the data type of XML-attributes is given as string. After the XML input model file is parsed, individual XML tags are parsed and translated to the respective IOM objects and stored as the internal object model in the memory. During the translation of input XML the string data types are also converted to appropriate Java data types e.g. value of isStatic is given as “yes/no” in the XML, it’s converted to Java boolean (true/false) type. While generating the output the exporter reads the internal object model from the memory and generates appropriate documents accordingly.

Fig 4