 | Level: Intermediate Chris Gerken (cgerken@us.ibm.com), Senior Software Engineer, IBM Roland Barcia (barcia@us.ibm.com), Certified IT Specialist, IBM
25 Oct 2006 This series exposes the practicality and benefits of model-driven development using the Design Pattern Toolkit (DPTK) to create pattern templates that capture your best practices. Accessing your model data is the key to writing good templates, and so this installment describes the query language and the special model and data access tags provided by the DPTK that give you direct access to the information you need to create effective and efficient templates.
From the IBM WebSphere Developer Technical Journal.
Introduction
The Design Pattern Toolkit (DPTK) is an Eclipse-enabled template engine for generating applications based on customizable, model-driven architecture transformations. We introduced the basics of the code generation facility in the DPTK in a previous article. In this article, we will discuss details on how you can access the model data from your templates.
Authoring Design Pattern Toolkit (DPTK) patterns requires familiarity with two subject areas: how to access the data model and how to use the available DPTK tags. We will focus here on the first topic, discussing the DPTK query language (which is very similar to abbreviated XPath), along with several simple tags that read the model and write the data directly to the template output.
Visit IBM alphaWorks to learn more about or to download the Design Pattern Toolkit.
The model and the data access tags
The data model in DPTK is presented to the DPTK templates as a document object model (DOM). The data usually comes from an XML file or XML string, but it can come from other sources as well, such as Java™ source files or flat files. Even DPTK templates have been used as a source for the model in patterns, such as the one that migrates DPTK patterns to JET patterns.
Although the data model can originate from any of many possible sources, it is always accessed as a DOM, and that DOM is always accessed using DPTK tags that accept query expressions as one or more attributes.
There are three simple DPTK tags that read from the model and write that information to the template's output. Each tag has a node attribute that takes a query expression describing a single model node in the DOM. Once the node is found, the tag performs its specific action against that node.
Each of the three tags writes a different aspect of the specified model node:
<content> - writes the content (the text between the start and end tag in an XML file) for the one node described by its node attribute.
<attr> - writes the value of one of the attributes for the specified node.
<dump> - writes an XML representation of the DOM subtree under the specified node.
Template authoring becomes straightforward with these tags, if not a bit tedious. As you determine which sections of the generated artifact content is dynamic, you build out the model of exactly what dynamic data is needed by the template. The dynamic content sections of the template are easily replaced by the appropriate <content>, <attr> or <dump> tags that use query expressions to target the correct model nodes. It is here, though, that a common question is asked: How do you write the query expression to get the correct node from an arbitrarily complex DOM?
An example
Import the Data Model Pattern project from the download file included with this article, dataModelPattern.zip, using the Import From Project Interchange wizard.
Open template write.pat in an editor to see an example of each of the three DPTK tags that write out model data (Figure 1).
Figure 1. write.pat template
A sample input model for this pattern can be found in simple.xml:
Figure 2. simple.xml
You can transform the sample input model with this pattern by selecting the model (file simple.xml) and using the right mouse button to select Apply Pattern from the context menu.
Figure 3. Transform template with pattern
You will be prompted to select the pattern with which to transform the model (Figure 4).
Figure 4. Available patterns
Select Data Model Pattern and then OK. You should see an informational message box telling you that the pattern was applied successfully.
Note that as a result of the pattern being applied, a new project named Results, containing a file named write.txt, has been created. When you open file write.txt, you will see how the DPTK tags in template write.pat, above, wrote out data that came from the input model in simple.xml.
Figure 5. write.txt
 |
DPTK query expressions
A DPTK query expression is a string that describes how to traverse the DOM. A traversal can end at a single node, or at several nodes, or at no nodes at all. We say that the query expression results in the set of nodes at which the navigation ends. When you pass a query expression into a tag through the node or nodes attributes, you're indicating that the tag should traverse the model, as described by the query expression, and act against the resulting nodes.
By convention, the node attribute specifies a query expression that should result in exactly one node. If the expression results in more than one node, then only the first node is used. If no nodes result, then an error is thrown. Meanwhile, the nodes attribute specifies a query expression that can result in any number (including zero) of nodes.
Syntactically, a query expression has a start node, followed by a sequence of steps that describe how to move from one node to another related node. There are two kinds of start node, differentiated by syntax:
- To start at the document node (the parent of the high-level element in an XML input), simply begin the query expression with a forward slash ("/").
- To start at any other node in the DOM, start the query expression with a variable name that will be associated with that node when the query expression is interpreted. Tags such as <iterate>, <useNode>, <exists> and <extend> can be used to associate variable names with specific nodes.
The query expression is processed by building a sequence of node collections:
- The first collection contains only the start node.
- The second collection contains all nodes that can be reached from the start node in the manner described by the first step.
- The third collection contains all nodes that can be reached from any of the nodes in the second collection in the manner described by the second step.
- The final collection of nodes gathered during the last step in the query expression is the set of resulting nodes for the query expression.
- If there are no steps in the query expression, the query expression results in the start node, pending any additional filtering.
The DPTK query expression language provides for several kinds of steps with which to specify model traversals:
Parent step: move from a node to that node's parent: syntax: ..
Child step: move from a node to all of that node's child nodes that have a given name: syntax: child-node-name
All children step: move from a node to all of that node's children: syntax: *
Figure 6. Query expressions
Figure 6 shows two DPTK query expressions made up of different kinds of steps. The first, /portlet/*/link toPage='6' starts at the document (the parent of the portlet element) and has three steps:
The first step, portlet, collects all nodes named "portlet" that are children of the document.
The second step, "*" navigates to all child nodes of the node named "portlet".
The third step, link toPage='6', navigates to all page node children that are named "link" and which have an attribute toPage whose value is '6'.
The second query expression, curlink/../.., starts at the node associated with variable curlink (the absence of a leading forward slash means the first token is a variable name) and navigates to the parents of the parents of that start node. It's up to the pattern author to ensure that the specified variable name is correctly associated with the correct node by previous DPTK tags.
Exercises
Listed below is the content of the file complex.xml in the sample pattern you loaded into Eclipse. This file stores information on the members of a swim team.
Listing 1
<swim>
<meet host-team='02' pool="Milburne" >
<event number="1" gender="F" distance="400" stroke="medly" relay="true" />
<event number="2" gender="M" distance="400" stroke="medly" relay="true" />
<event number="3" gender="F" distance="100" stroke="freestyle" relay="false" />
<event number="4" gender="M" distance="100" stroke="freestyle" relay="false" />
<event number="5" gender="F" distance="100" stroke="butterfly" relay="false" />
<event number="6" gender="M" distance="100" stroke="butterfly" relay="false" />
<event number="7" gender="F" distance="100" stroke="backstroke" relay="false" />
<event number="8" gender="M" distance="100" stroke="backstroke" relay="false" />
<event number="9" gender="F" distance="100" stroke="breaststroke" relay="false" />
<event number="10" gender="M" distance="100" stroke="breaststroke" relay="false" />
<event number="11" gender="F" distance="400" stroke="free" relay="true" />
<event number="12" gender="M" distance="400" stroke="free" relay="true" />
<swimmer name="Fred" teamref="01">
<entry event="2" />
<entry event="10" />
<entry event="12" />
</swimmer>
<swimmer name="Sam" teamref="02">
<entry event="6" />
<entry event="8" />
</swimmer>
<swimmer name="Mary" teamref="01">
<entry event="5" />
<entry event="9" />
<entry event="11" />
</swimmer>
<team id="01" name="Eastwood" />
<team id="02" name="Westlake" />
</meet>
</swim>
|
The data has been normalized so that values are stored only once, requiring the use of foreign keys in various elements. For example, to get the name of Mary's team, you have to find the <team> element whose ID value is 01. Likewise, to find her events, you need to iterate over the <entry> elements nested in her <swimmer> element, and look up the <event> element whose number attribute matches the entry's event attribute.
Pattern templates often use iteration and filtering techniques to correctly generate artifacts. We will build up an example of a template that generates a report listing all teams, all swimmers for each team, and the events in which each swimmer will swim.
We start with an iteration over all of the teams. To get the collection of elements representing teams at the meet, we must start at the document and navigate through elements <swim>, <meet> and <team>, in that order. The DPTK query expression describing that navigation is /swim/meet/team, and we use that expression in the nodes attribute of an <iterate> tag:
<iterate nodes="/swim/meet/team" name="team">
</iterate>
|
The <iterate> tag will collect all nodes that match the query expression. For each node in that collection, the tag will associate that node with the name "team" and will process its contents once.
For each team, we want to write out a line with the team's name followed by a listing of swimmers for that team. Remembering that the <team> element is associated with variable name "team", we can use that variable name when referring to the team's attributes:
<iterate nodes="/swim/meet/team" name="team"> |
Team: <attr node="team" name="name"/>
</iterate>
In the <attr> tag, the query expression "team" doesn't start with a forward slash, so the first token, "team", is taken to be a variable name. Since there are no steps following the variable name, this query expression results in a single node which happens to be the current <team> node. The <attr> tag has the effect of writing out the name attribute of the current team.
Once we've written the team's name, we need to iterate over the swimmers on the team. To collect the nodes representing all of the <swimmer> elements associated with a given <team> element, we need to navigate from the document through elements <swim>, <meet>, and <swimmer>. When stepping from the <meet> element to the <swimmer> element, we only want <swimmer> elements whose teamref attribute matches the current team's ID attribute.
The query expression that performs this navigation uses three steps (the last one has a filter): /swim/meet/swimmer teamref='%team(id)%' . We nest an <iterate> tag inside the first <iterate> and indicate that within the scope of the iteration, variable name "swimmer" will be associated with the current <swimmer> element.
<iterate nodes="/swim/meet/team" name="team">
Team: <attr node="team" name="name"/>
<iterate nodes="/swim/meet/swimmer teamref='%team(id)%'" name="swimmer">
Swimmer: <attr node="swimmer" name="name" />
</iterate>
</iterate>
|
We will create a template in our data model pattern to hold the above template section:
Create a new file in the project root directory and name the new file swimming.pat.
Figure 7. Create a new file
Once you have created the new template, you need to edit the control.pat file, which controls which templates get applied to the model. Since we're using a different model than before, you don't want to apply the write.pat template, which assumes the old model. When you edit the control.pat template, you just have to change the one <start> tag:
Figure 8. Modify start tag
You can now apply the pattern to the model in the complex.xml file. The pattern now generates the file swimming.txt.
Figure 9. Apply pattern to the model
The last thing the template has to do is list the events for each swimmer, but the navigation to do this is a bit more complex. Each <swimmer> element has some number of nested <entry> elements that hold the value with which to search for the correct <event> element. The navigation used by the iterate starts at the current swimmer and traverses down to all nested <entry> elements. The query expression for this is swimmer/entry.
For each <entry> element, navigate to the correct <event> element by starting at the document node and traversing <swim>, <meet>, and <event> elements. The final step is also filtered using the <event> number attribute and the <entry> event attribute. The template now looks like this:
Figure 10. Template with complex navigation
And it produces an output file like this:
Figure 11. Template output
Notice that the three <attr> tags for the event all have the same query expression. We can simplify the template considerably with a <useNode> tag inside the inner <iterate> tag to temporarily associate the correct <event> element with the variable name "event":
Figure 12. Simplified template
Now that the <attr> tags take less space, it's easier to make the template produce a more nicely formatted output:
Figure 13. Template output
 |
Conclusion
Understanding how to access your model data is the key to writing good templates. The Design Pattern Toolkit provides special model and data access tags that can access elements from your internal model, which is represented as a DOM. The DPTK query language further compacts your templates by enabling you direct access to almost anywhere in the DOM with a single string. This article described the DPTK expression language and demonstrated how it can be applied to create an effective way of accessing your data model.
Acknowledgements
The authors would like to thank Geoffrey Hambrick for his review of this article.
Download | Description | Name | Size | Download method |
|---|
| Code sample | DataModelArticle.zip | 7 KB | HTTP |
|---|
Resources
About the authors  | |  | Chris Gerken is a member of the Asset Reuse Enablement Team within the IBM Software Services for WebSphere group. He created and wrote the Design Pattern Toolkit. |
 | |  | Roland Barcia is a Certified IT Specialist for IBM Software Services for WebSphere in the New York/New Jersey Metro area. He is a co-author of IBM WebSphere: Deployment and Advanced Configuration. For more information on Roland, visit his Web site. |
Rate this page
|  |