JetSet Tutorial

by Helge Timenes
Last Updated: March 31st 2009




Executive Overview

JetSet is a set of Eclipse plug-ins for quickly building XML to ASCII and XML to XML model transformation tools. You do not have to know much about XML to use it, but it is useful to know what a Meta-Model and XML Schema is.

When working with JetSet you will need a source meta-model that describes the structure of the models you want to transform. Although not required, it will make your life simpler if you also have a target meta-model describing the desired structure of your models after transformation. JetSet can work with two types of meta-models: XML Schema and Ecore

For the purpose of this tutorial, we will use two XML-Schemas: The first describes the structure of a purchase order for a imaginary company. This will be our source meta-model. The second describes the structure of a simple document format, invented for the sake of this tutorial. This will be our target meta-model. The tutorial will show how to create a tool that will transform any model conforming to the purchase order source meta-model into a report conforming to our simple document format.

Content

  1. Preparations
  2. The Source Meta-Model
  3. The Target Meta-Model
  4. Creating the JetSet
  5. Building the Transformation Specification Tree
  6. Generating the Transformation Tool
  7. Testing the Transformation Tool
  8. Conclusions



1 Preparations

Requirements

This tutorial was written using the following software configuration:

Installation

  1. Download Eclipse (Windows users can use the link above)..
  2. Install Eclipse (Windows users: Simply extract the downloaded file eclipse-jee-ganymede-SR1-win32.zip to a directory of your choice, e.g. C:\Program Files).
  3. Install JetSet by adding the JetSet update site to the list of sites where Eclipse shall look for updates:
    • Select Software Updates...from the Help menu.
    • Select the Available Software tab
    • Click on Add Site...
    • Enter the URL to the JetSet update site: http://jetset.sf.net/update
    • Expand the JetSet entry (make sure you have a connection to the internet) and select the check box for JetSet version 0.4.0
    • Click on Install...
Figure 00
Figure 00: Adding the JetSet update site.

2 The Source Meta-Model

This tutorial will use as source meta-model the PurchaseOrder.xsd XML schema. Briefly it describes the required structure of a purchase order containing a ship-to address, a bill-to address and a list of purchased items. (Listing 1).

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
            xmlns:po="http://www.example.org/po"
            targetNamespace="http://www.example.org/po">
 
  <xsd:element name="purchaseOrder" type="po:PurchaseOrder" />

  <xsd:complexType name="PurchaseOrder">
    <xsd:sequence>
      <xsd:element name="shipTo" type="po:Address" 
                   minOccurs = "1" maxOccurs="1" />
      <xsd:element name="billTo" type="po:Address" 
                   minOccurs = "1" maxOccurs="1" />
      <xsd:element name="items" type="po:Item" 
                   minOccurs="1" maxOccurs="unbounded" />
    </xsd:sequence>
    <xsd:attribute name="orderDate" type="xsd:date" />
  </xsd:complexType>

  <xsd:complexType name="Address">
    <xsd:sequence>
      <xsd:element name="name" type="xsd:string" />
      <xsd:element name="street" type="xsd:string" />
      <xsd:element name="city" type="xsd:string" />
      <xsd:element name="postalCode" type="po:PostalCode" />
      <xsd:element name="country" type="xsd:string" />
    </xsd:sequence>
  </xsd:complexType>

  <xsd:complexType name="Item">
    <xsd:sequence>
      <xsd:element name="productName" type="xsd:string" />
      <xsd:element name="quantity" type="xsd:positiveInteger" />
      <xsd:element name="price" type="xsd:float" />
      <xsd:element name="shipDate" type="xsd:date" />
    </xsd:sequence>
    <xsd:attribute name="partNum" type="xsd:string" />
  </xsd:complexType>

  <xsd:simpleType name="PostalCode">
    <xsd:restriction base="xsd:string">
      <xsd:pattern value="[A-Z]{2}\d{5}" />
    </xsd:restriction>
  </xsd:simpleType>
  
</xsd:schema>
Listing 1: PurchaseOrder.xsd

2 The Target Meta-Model

The goal of our tutorial is to create a model transformation tool that creates a purchase report for all purchase orders of the type described by Listing 1 above. The format of these reports is a simple XML document that contains a number of objects, and where each object is either a text paragraph or a table. For this purpose we will use SimpleDoc.xsd (Listing 2) as target meta-model for our transformation tool.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 
    targetNamespace="http://www.example.org/doc" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:doc="http://www.example.org/doc">
    
 <xs:element name="simpleDoc" type="doc:SimpleDoc" />
 
 <xs:complexType name="SimpleDoc">
  <xs:sequence>
   <xs:element name="object" type="doc:Object" 
       minOccurs="1" maxOccurs="unbounded" />
  </xs:sequence>
 </xs:complexType>
 
 <xs:complexType name="Object" abstract="true">
  <xs:attribute name="title" type="xs:string" />
 </xs:complexType>
 
 <xs:complexType name="Paragraph">
  <xs:complexContent>
   <xs:extension base="doc:Object">
    <xs:attribute name="text" type="xs:string" />
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>
 
 <xs:complexType name="Table">
  <xs:complexContent>
   <xs:extension base="doc:Object">
    <xs:sequence>
     <xs:element name="row" type="doc:Row" 
         minOccurs="0" maxOccurs="unbounded">
     </xs:element>
    </xs:sequence>
   </xs:extension>
  </xs:complexContent>
 </xs:complexType>
 
 <xs:complexType name="Row">
  <xs:sequence>
   <xs:element name="cell" type="xs:string" 
       minOccurs="1" maxOccurs="unbounded">
   </xs:element>
  </xs:sequence>
 </xs:complexType>
 
</xs:schema>
Listing 2: SimpleDoc.xsd

4 Creating the JetSet

First we need to create a standard empty Eclipse project in which our JetSet can live.
  1. Click on File → New → Project... from the Eclipse menu.
  2. Select Project from the General category.
  3. Fill in a project name (for example "JetsetTutorial")
  4. Click on the Finish button.
Figure 01
Figure 01: Creating a new project.
Now we need to import the source and target meta-models. The simplest way to do this is just to create two empty files and paste the content of listing 1 and to into them.
  1. Right-click on the newly created project
  2. Select New → File.
  3. Enter PurchaseOrder.xsd as file name.
  4. Click Finish. This will open up the Eclipse XML editor with an empty file.
  5. Click the Source tab at the bottom of the XML editor.
  6. Paste the content of Listing 1 into the empty file and save it.
  7. Repeat steps 1-6 for Listing 2, using SimpleDoc.xsd as file name.
Alternatively, you can download them here:

Your project workspace should now look something similar to Figure 02 below.

Figure 02
Figure 02: Project and meta-models.
Now we are ready to create the JetSet model. Note: The JetSet model is not the actual model transformation tool, rather it is a specification of a model transformation tool, from which the actual tool will be generated.
  1. Right-click on the newly created project
  2. Select New → Other... and locate the JetSet entry as shown in Figure 03.
Figure 02
Figure 03: Create a new JetSet.
  1. Click on Next.
  2. Enter a file name width a ".jetset" extension (e.g. "Tutorial.jetset").
  3. Click on Next.

You should now be presented with the following dialog (Figure 04):

Figure 03
Figure 04: Configuring your JetSet.

In the Source meta model field you have to enter the path to the meta model you wish to use as source meta-model. This path can be either an absolute system path or a relative path within your workspace. The Browse... button will let you browse for the meta model, either through the file system or your eclipse workspace, depending on which of the two radio buttons are selected.

Similarly, the Target meta model field lets you specify your target meta model.

  1. Click the first Browse... button and select PurchaseOrder.xsd as source meta-model
  2. Click the second Browse... button and select SimpleDoc.xsd as target meta-model
  3. Click the Finish button.
Figure 05
Figure 05: JetSet configured.

After clicking the Finish button, JetSet editor will be opened showing the JetSet that you just created (Figure 06).

Figure 06
Figure 06: The JetSet Editor.

The JetSet editor consists of a tree where each node is associated with a type in the source meta model. Lets examine it a bit closer:

  1. Right click on the Document Root node
  2. Select Show Properties View.

You should see something similar to Figure 07.

Figure 07
Figure 07: The Properties View.

JetSet always tries to give nodes sensible names, in most cases derived from the source type with which the node is associated, which in this case is org.example.po.DocumentRoot. This might seem odd, as there is no type named DocumentRoot in our purchase order schema. The reason is that behind the scenes JetSet convert all XML schemas to Ecore models, which are more expressive and easier to work with. The DocumentRoot is an artifact of this process and is simply a type that contains all the top level elements defined in the XML schema. More about this shortly.

Let's take a closer look at the properties of this node:

  • Node Name: The Node Name property is the name of the node. When adding new nodes to your tree, JetSet will give the new node a default name derived from the model class associated with the node. This can be overridden at any time by changing the value of this property.
  • Source Meta Model: The Source Meta Model property defines the structure of the inputs of your model transformation. It is the same as was specified in the wizard earlier. If desired you can change the meta-model at any time by changing this property, but beware that this may cause some inconsistencies (not covered in this tutorial).
  • Source Root Type: The Source Root Type property defines your entry point in the models. In most cases this will be the DocumentRoot, but it need not be. The editor for this property lets you select any type defined in your source schema and, if not the document root, your transformation will act on any instance of the selected type and their contents.
  • Target Meta Model: The Target Meta Model property defines the structure of transformed models. It is the same as was specified in the wizard earlier.
  • Output Project Name: Based on the tree you define in the editor, JetSet will generate a standard Java project that is the actual model transformation tool. The property Output Project Name is the name of this project. The default value is derived from the names of your source and target meta models, in our case PurchaseOrder and SimpleDoc.
  • Output Project Namespace: All Java classes generated in the output project will use the same name space (i.e. belong to the same Java package). The property Output Project Namespace determines what this will be. Change the value of this property to whatever suits you. I will stick to the default "com.mycompany" for this tutorial.
  • Output Project Main Class Name: This property defines the name of the Java class containing the main() method in the resulting generated output project. When everything is finished you will execute your transformation with java [YourMainClassName].
  • Project Dependencies: The Project Dependencies property allows you to specify existing Java projects containing utility classes you want to use from within your JetSet. Multiple projects are separated by commas. This is considered advanced usage and is not part of this tutorial.
  • Imports: If you do use classes located in other projects, you will need to specify the package(s) to which they belong. This is what the Imports property is for. Multiple packages are are separated by commas. This is considered advanced usage and is not part of this tutorial.

5 Building the Transformation Specification Tree

Now to the really interesting part: Defining the model transformation specification. With JetSet this is done by adding nodes to the project node we created in the previous step. 8 different types of node exist, each providing different possibilities to extract information from the model:

  • Project Node Icon The Project Node is the root node of the JetSet. As described in the previous section, it contains information pertaining to the entire JetSet tree. There is only one project node per JetSet
  • File Node Icon File Nodes are used to define the the output files of the transformation process. A JetSet must contain at least one file node (otherwise nothing would be produced. The file names may be derived from model variables, as we will see soon.
  • Iteration Node Icon Iteration Nodes define iterations over references or many-valued attributes. For instance, in the purchase order meta-model used in this tutorial, the type PurchaseOrder has a reference to the type Item called items. You can use an iteration node to define a transformation of the type For each item in a purchase order, do...

    Note: If an iteration is defined over a reference that can have only one value, the icon of the iteration node changes slightly to indicate this: Iteration Node Icon
  • Text Node Icon Text Nodes, along with Xml Nodes, are the real work horses in the JetSet family. They are used to define any unstructured ASCII content you might want in your output files. The text of a text node must be entered exactly as you want it to appear in your output, with variables (e.g. model attributes) wrapped in <% %> brackets. (This is the Java Emitter template (JET) format that has lent its name to this tool.) Text nodes are useful if you do not have a target meta model and just want to transform your source models to any ASCII format. If you DO have a target meta model, XML Nodes are your best friend.
  • Xml Node Icon Xml Nodes are similar to the text nodes except that they impose constraints on your outputs such that it complies to the structure of your target meta model. Each Xml Node will produce an XML element in your output. Which element is produced you can specify in the properties of the node, but it is limited to the element relations specified in your target meta model. The Xml Node offers a list of properties corresponding to the attributes of the selected target type, and lets you fill this with values derived from your source model (using the JET notation mentioned above). The Xml Node also takes care of closing brackets and tags as well as indenting the text based on where in your target meta model you operate.
  • Subtype Filter Node Icon Subtype Filter Nodes are useful for source meta models that use abstract types and inheritance. Their use is not covered by this tutorial.
  • Link Node Icon Link Nodes are used to jump from one location in the tree to another. This is handy when the same type is being used several places in the meta model, or if the meta model contains recursive references. Their use is not covered by this tutorial.
  • Link Target Node Icon Link Target Nodes are, as the name implies, the target to which a link node links. A link target node can be linked to from multiple link nodes, provided the link node is defined for the same type as the target link node. NOTE: Link target nodes can only be added to the project node of the JetSet.
  • Group Node Icon Group Nodes have no generative effect, they are simply a way organizing your JetSet tree, should it become unreadable through excessive use of the other nodes :-).

Returning to our purchase order example, let us define a simple JetSet that transforms any given model conforming to the PurchaseOrder.xsd schema into a report conforming to the SimpleDoc.xsd schema.

  1. Right-click the project node (named System)
  2. Select New Child → Iteration Node.

A new node will be added to the JetSet tree with name defaulting to Purchase Order (Figure 08). This is because JetSet, knows that the purchaseOrder element (of type po:PurchaseOrder) is top level element of the source meta model and thus the only possible reference from the DocumentRoot type. Furthermore, this is a single-valued reference, hence the little "1" in the icon. If you want to be picky you might say that you can't iterate over single-valued references, as they will always have only one instance, but for the sake of simplicity JetSet handles multi-valued and single valued references alike (except for the node icon).

Figure 08
Figure 08: Adding an Iteration Node.

Now let us define out output artifact: A XML file where the a purchase report in the SimpleDoc format will be generated

  1. Right click on the System Target node.
  2. Select New Child → File Node.
  3. Rename the new node System Output File.
Figure 11
Figure 11: The File Node.

The file node's default name is Purchase Oder which reflects that the model object from which it can extract information is still of type PurchaseOrder, as it is for the containing iteration node. Unlike iteration nodes, file nodes can not be used to navigate the source model. They only specify that at this point a file will be generated.

To make things a bit more readable, let us change the node name to "Purchase Report" as this is what our file node will represent. The name of the nodes in your JetSet tree will have no effect on the transformation process. They are only there for making your tree more readable and easier to understand.

  1. Type "Purchase Report" in the value field for Node Name.

Now, let us set the file name property such that each purchase report will be generated as in a file named after the purchase date, which will be pulled from the model during model transformation.

  1. Click the ... button in the value field of the Output File Name.
  2. In the text field dialog that appears, write PO_.
  3. Select pull-down menu called Attribute and select orderDate.
  4. Click on Insert.
  5. Add a file extension by typing .xml after the inserted blue string.
  6. Make sure things look similar to Figure 10 below and click on Close.
Figure 10
Figure 10: File name with date pulled from model.

We have now specified that the file name of each purchase report generated from any purchase order will be on the form PO_2008-10-16.

There are a few things worth mentioning about what we just did. First of all, the string that was automatically inserted is delimited by <% and %> and shown in blue color. This is the JET syntax mentioned before. Everything inside the <% %> delimiters must be valid Java code. If you are not a Java programmer, don't worry about the next paragraph: The attribute selection and formatting pull-downs and insert button should cover most of your needs.

Note for Java programmers: These little blue lines of code should be an invitation to deploy your crazy hacker skillz. The getValue(String attributeName) method is a built in convenience for extracting data from your model. It always returns a string containing the attribute value (empty string if not found). If you want to modify the model object directly, you can access it by calling modelStack.peek(), which returns an instance of the EMF class EObject. You can immediately manipulate the model object or its attributes using all java.lang and java.util classes. More, including your custom code, can be added with the imports property of the Project Node.

Back to the JetSet tree we now have the following (Figure 11):

Figure 11
Figure 11: File node attributes set.

Still, a generator based on this tree would not really generate anything useful. Just one empty file per purchase order. But before we continue, you may have noticed the little asterisk next to the file name in the editor tab? This means that it is time to save the work we have done so far and go and get another cup of coffee...

  1. Press Alt + F + S.
  2. Grab coffee (required).
  3. Call your wife, tell her you love her and make sure your 18 months old son is doing just fine (optional).
  4. Go back to your desk and continue this tutorial.

Now we want to specify the content of our purchase report. This is where the target meta model comes in to play. To utilize it, we need to add some Xml Nodes to out tree, the first of which will define the top level element of our SimpleDoc XMl file:

  1. Right click on the File Node (named "Purchase Report").
  2. Select New Child → Xml Node.
  3. Right click on the newly created Xml Node (named "Simple Doc").
  4. Select New Child → Xml Node.

You should now see the following (Figure 12):

Figure 12
Figure 12: Xml Nodes.

The node named "Simple Doc" will produce the top level <SimpleDoc> element of our purchase report, complete with name-space declaration, schema reference, angle brackets and closing tag. The "Object" node will create a <object> element of the type "Paragraph" within the <SimpleDoc> element (remember, the SimpleDoc schema specifies that the document can have two types of objects: paragraphs and tables). If we wanted our first document object to be a table instead of a paragraph, all we would have to do would be to change the value of the Target Element

Notice the two last properties of the Xml Node called "Object". Unlike other nodes the Xml Nodes may have dynamic properties specific to that node. These correspond to the attributes of the target element that the node represents. In SimpleDoc.xsd we specified that all objects have a title attribute, and that paragraphs, extending the abstract <Object> type, also have a text attribute. The values of these we can now define with these corresponding properties.

Let us give our first paragraph the static (equal for all purchase reports) title "Purchase Report" and a text describing the date at which the purchase order was placed. Also let us rename the node to "Header" to make the tree more readable.

  1. Click the ... button in the value field of the Attribute 'title' property.
  2. Enter Purchase Report in the text dialog and click close.
  3. Click the ... button in the value field of the Attribute 'text' property.
  4. In the text dialog enter Date: (with a trailing space).
  5. Select pull-down menu called Attribute and select orderDate.
  6. Click on Insert and then Close.
  7. Click in the value field of the Node Name property and enter "Header Paragraph".

The properties for the "Object" node now looks like Figure 13:

Figure 13
Figure 13: Header paragraph properties.
Now let us add two more paragraph nodes, one for displaying the Ship-To information from of the purchase orders, and one for the Bill-To information:
  1. Add a new Iteration node to the "Simple Doc" Xml Node (this should be named "Ship To" by default)
  2. Add a Xml Node node to the new "Ship To" Iteration Node
  3. Rename the newly created Xml Node to "Ship To Paragraph"
  4. Open the editor for the Attribute 'title' property of the "Ship To Paragraph" node and enter the (static) text Ship To: with a space at the end.
  5. Open the editor for the Attribute 'text' property of the "Ship To Paragraph" node.
  6. Select name from the attribute pull-down list and click Insert
  7. Add a space and a comma after the blue text
  8. Repeat steps 6 and 7 for the attributes street, postalCode, city and country

Your tree should now look like Figure 14 below

Figure 14
Figure 14: Ship-To paragraph.
As the Bill-To information is structured similarly to the Ship-To information in all purchase orders, we don't have to repeat the above steps to set up a Bill-To paragraph. Instead we can copy the Ship-To nodes and just make some minor adjustments:
  1. Select the "Ship To" iteration node.
  2. Drag it up to the "Simple Doc" node and release it there. You should now have two "Ship To" nodes and two "Ship To Paragraph" nodes.
  3. Change the value of the Iteration Reference property from "shipTo" to "billTo" (node name will be adjusted automatically).
  4. Rename the "Ship To Paragraph" node under the "Bill To" iteration node to "Bill To Paragraph"
  5. Change the (static) value of the Attribute 'title' property of the "Bill To Paragraph" node to "Bill To: "

Your tree should now look like Figure 15 below

Figure 15
Figure 15: Bill-To paragraph.
We now have the specifications for a model transformation tool that produces simple purchase reports with ship-to and bill-to information in nice little paragraphs. But we also want to report which items where purchased. For this we will want to use a table with a static set of column headers and then one row for each purchased item.
  1. Add a new Xml Node to the "Simple Doc" node.
  2. Change the value of the Target Element property to
    "object (doc:Table)"
  3. Name the node "Items Table"
  4. Add a new Xml Node to the newly created "Items Table" node and name it "Header Row"
  5. Add 6 (six) Xml Nodes to the newly created "Header Row" node and name them "Ship Date Header", "Part No Header", "Product Name Header", "Unit Price Header", "Quantity Header" and "Total Price Header" respectively.
  6. Set the Attribute 'text' properties of the 6 new nodes to "Ship Date", "Part No.", "Product Name", "Unit Price", "Quantity" and "Total Price" respectively.

Your steadily growing tree should now look like Figure 16 below:

Figure 16
Figure 16: Static column headers.

Now all we need to finish our task is an iteration over all the items in the purchase order and extract the information corresponding to our 6 columns for each of them.

  1. Add a new Iteration Node to the "Items Table" node.
  2. Change the value of the Iteration Reference property to "items (po:Item)". Notice how the icon changes to let us know that "items" is a many-valued reference (i.e. there can be many items in a purchase order)

We know have a tree like the one depicted in Figure 17:

Figure 17
Figure 17: The "Items" iteration.

Lets take a break here to look a bit closer on the Iteration Node. It has several properties, most of which are optional (the mandatory properties are set by default). Here is a brief summary of what these properties are for and how they can be used while iterating through a model:

  • The Node Name property specifies the name of the node. Change this if the default suggestion does not make sense.
  • The Iteration Reference property specifies over which reference the iteration node shall iterate. In the value-column, a list will be provided containing all references and many-valued attributes that are specified in the source meta model for the type in question.
  • The Condition Filter provides means to exclude certain model object from the iteration. If, for instance, you would later like all items whose price is zero to be excluded from your final output, this would be the place to do it.
  • The Pre- and Post Iteration Text properties allows you to define special output immediately before and after the iteration. For instance, if you are generating a list in HTML, this would be a good place to put <ul> and </ul> elements. Note that the pre- and post iteration text is not generated if the iteration is "empty" (i.e. the particular model instance does not contain any instances of the referenced type).
  • The No Iteration Text property lets you define special output for "empty" iterations (i.e. where the particular model instance does not contain any instances of the referenced type).
  • The Sort Criteria property can be used to sort the referenced model elements before iteration. If for instance you would want to list all systems, sorted alphabetically by name (or any other attribute) this can be done here.
  • The Sort Descending property only applies if a sort criteria has been defined. If so you can reverse the order here.

Let's use the sort criteria to make sure that all items in our table are listed in order of their ship date, with the most urgent item on the top

  1. Open the editor for the Sort Criteria property of the "Items".
  2. Select shipDate in the attributes pull-down and click Insert and then Close. This will ensure that all items are processed in order based on their ship date, with the first item to be shipped first. Naize.

Let us now complete our JetSet tree by adding a row in our table for each item with cells that derive their content from the purchase order models.

  1. Drag the "Header Row" to the "Items" iteration node and release it there while holding down the Ctrl key, creating a copy.
  2. Rename the second "Header Row" node to "Item Row"
  3. Remove the "Header" part in the names of the 6 nodes underneath the new "Item Row"
  4. Open the editor for the Attribute 'text' property of the "Ship Date" node (the first of the 6 cells)
  5. Delete the old text, select shipDate in the Attribute pull-down, click on Insert and then Close
  6. Repeat step 5 for the next four nodes, selecting the attributes partNum, productName, price and quantity.
  7. For the sixth node we want to use information that is not present in the models, namely the total price of the item order. This can however be derived as the product of quantity and price. To achieve this, we need to parse the value of these these from String to Integer and Double which can be multiplied. For simplicity just enter the following into the editor for the Attribute 'content' property of the "Total Price" node:
    <%=Integer.parseInt( getValue("quantity") ) * Double.parseDouble( getValue("price") )%>

Your final JetSet tree, the specification for a model transformation from PurchaseOrder to SimpleDoc should now look like Figure 18 below:

Figure 18
Figure 18: The complete JetSet tree.

Save your work, get more coffee and brace yourself for the magic of section 6!

6 Generating the Transformation Tool

We have now a complete specification of a transformation from purchase orders compliant to the PurchaseOrder.xsd schema to documents compliant to the SimpleDoc.xsd schema. Now we can generate the actual tool:

  1. Right click on any node in the JetSet tree.
  2. Select JetSet → Generate Model Transformator.

You will notice that a new Project called PurchaseOrderToSimpleDocTransformator has appeared in the project explorer view (Figure 17). As mentioned at the start of this tutorial, the name of this project is taken from the Output Project Name property of the Project Node ("DocumentRoot" in our case).

Figure 19
Figure 19: The generated transformator project.

This project is the actual model transformation tool: It is a standard java project containing several classes generated from the nodes in the JetSet tree, plus one main class named PurchaseOrderToSimpleDocTransformator (also specified in the Project Node). This class contains a main function that takes three arguments:

  • Path to meta-model
  • Path to the model to be transformed
  • Path to a directory where the transformed artifacts shall be stored

This is as mentioned a normal java project and can be run within Eclipse like any other Java project. In many cases you would however want to trigger your model transformation from scripts or command line tools. JetSet offers a quick way to export this project with all its dependencies. It even generates a batch file to run it all. Let's try it.

  1. Right click on any node in the JetSet tree.
  2. Select JetSet → Export Stand-Alone Executable....
  3. Select a directory where you want the executable exported, for example C:\Temp\JetSetTest\.
  4. Click Ok

If you look in the directory you selected above you should now see the following files:

Figure 20
Figure 20: The stand-alone transformator executable and dependencies.
  • Here the net.sf.jetset.runtime_0.4.0.jar file is a run-time JetSet library needed for the execution of the transformation tool.
  • The org.eclipse... files are eclipse dependencies needed when running the transformation tool outside of eclipse.
  • The PurchaseOrderToSimpleDocTransformator.jar file is the transformation tool itself. It contains all classes generated from your JetSet tree.
  • Transformation.bat is a batch file to execute the transfromation tool with all its dependencies.
  • PurchaseOrder.xsd is a copy of our source meta model. This is needed to validate the input XML models before translation.

You now have all you need to create purchase reports in the directory you chose above.

7 Testing the Transformation Tool

To test our transformation tool we need a PurchaseOrder model instance. Any XML file following the format dictated by the PurchaseOrder.xsd meta model will do, but for this tutorial we can use the simple model given in Listing 11, describing a rather silly purchase made by the Sesame Street Cookie Monster and billed to Paula Kerg, the CEO of PBS. You can get it here or simply copy from Listing 3. Save it to a local file on your hard drive, for instance
C:\Temp\JetSetTest\PurchaseOrder.xml.

<?xml version="1.0" encoding="UTF-8"?>
<po:purchaseOrder 
  orderDate="2008-10-20" 
  xmlns:po="http://www.example.org/po" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://www.example.org/po PurchaseOrder.xsd ">
  <shipTo>
    <name>Cookie Monster</name>
    <street>Sesame Street</street>
    <city>New York</city>
    <postalCode>NY12345</postalCode>
    <country>USA</country>
  </shipTo>
  <billTo>
    <name>Paula Kerger</name>
    <street>2100 Crystal Drive</street>
    <city>Arlington</city>
    <postalCode>VA22202</postalCode>
    <country>USA</country>
  </billTo>
  <items partNum="376-UF">
    <productName>Small Cookies</productName>
    <quantity>150</quantity>
    <price>0.25</price>
    <shipDate>2009-01-07</shipDate>
  </items>
  <items partNum="326-MJ">
    <productName>Large Cookies</productName>
    <quantity>50</quantity>
    <price>0.50</price>
    <shipDate>2008-11-12</shipDate>
  </items>
  <items partNum="326-MJ">
    <productName>Christmas Cookies</productName>
    <quantity>25</quantity>
    <price>1.5</price>
    <shipDate>2008-12-24</shipDate>
  </items>
</po:purchaseOrder>
Listing 11: PurchaseOrder.xml.

Though you can run the transformation from within eclipse, we will test it in stand-alone mode, using the batch file we generated in the last chapter.

  1. Open up a command prompt console (Windows users: Start → Run and enter "cmd.exe").
  2. Change to the directory where the stand-alone executable and batch file was generated (e.g. cd C:\Temp\JetSetTest\).
  3. Trigger the transformation using the generated batch file with the name of the model file and name of directory where we want the output to be generated. For example
          transform PurchaseOrder.xml .
    to generate our output in the current directory

If we have done everything right so far, you will notice that a file "PO_2008-10-20.xml" has appeared in the directory you specified as output directory. Its content should be as shown in Listing 4 below. This is or transformed model, a purchase report, exactly as we wanted (notice that the the item rows in the table have been sorted according to ship date)

<doc:SimpleDoc 
   xmlns:doc="http://www.example.org/doc" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
   xsi:schemaLocation="http://www.example.org/doc SimpleDoc.xsd">
   <object xsi:type="doc:Paragraph"
       title = "Purchase Report"
       text = "Date: 2008-10-20" />
   <object xsi:type="doc:Paragraph"
       title = "Ship To: "
       text = "Cookie Monster, Sesame Street, NY12345, New York, USA" />
   <object xsi:type="doc:Paragraph"
       title = "Bill To: "
       text = "Paula Kerger, 2100 Crystal Drive, VA22202, Arlington, USA" />
   <object xsi:type="doc:Table">
      <row>
         <cell>Ship Date</cell>
         <cell>Part No.</cell>
         <cell>Product Name</cell>
         <cell>Unit Price</cell>
         <cell>Quantity</cell>
         <cell>Total Price</cell>
      </row>
      <row>
         <cell>2008-11-12</cell>
         <cell>326-MJ326-MJ</cell>
         <cell>Large Cookies</cell>
         <cell>0.5</cell>
         <cell>50</cell>
         <cell>25.0</cell>
      </row>
      <row>
         <cell>2008-12-24</cell>
         <cell>326-MJ326-MJ</cell>
         <cell>Christmas Cookies</cell>
         <cell>1.5</cell>
         <cell>25</cell>
         <cell>37.5</cell>
      </row>
      <row>
         <cell>2009-01-07</cell>
         <cell>376-UF376-UF</cell>
         <cell>Small Cookies</cell>
         <cell>0.25</cell>
         <cell>150</cell>
         <cell>37.5</cell>
      </row>
   </object>
</doc:SimpleDoc>
Listing 4: The final result: A purchase report conform to SimpledDoc.xsd.

8 Conclusion

We have seen how we can use JetSet to transform a structured model into another type of structured model using two meta-models to the describe these structures. JetSet offers a easy to understand graphical editor to define each step of the transformation, and does all the grunt work automatically. You no longer need to be an XML wizard to create a model transformation tool.

JetSet also offers more advanced features, such as using external Java code to enhance the model data extraction where the standard iteration based approach is not enough. This may be covered in a separate tutorial at some point. But then again, maybe it won't...

 

The JetSet model used to make this tutorial can be found here