Copyright © 2008
New Technology / enterprise Ltd.
Objective
Here we are going to extend some of the ideas in the last tutorial to look at model-driven transformation. In this case we will be using information in an XML model to create a very small, very basic web-site.
In the previous tutorial we used a single dummy node called root; now we will be extending that to a model, used to create an object tree. Rendering the output files will take place by walking the tree looking for matching template subdirectories that have an ant build file. The ant job creates the output files for each matching object node using JeeWiz templates as before.
The Model
The XML model looks like this:
<site name="My Site">
<logo image="myLogo.jpg"/>
<site-menu>
<menu-item position="1"
link-caption="Home"
page="index"
/>
<menu-item position="2"
link-caption="FAQ"
page="faq"
/>
<menu-item position="3"
link-caption="Contact Us"
page="contact"
/>
</site-menu>
<screen name="faq"
title="Frequently Asked Questions"
>
<section title="How do I Use a Model?" >
You separate the logical semantic from implementation and put the semantic breakdown into the model, leaving it open how the architecture will handle it.
</section>
</screen>
<screen name="index"
title="My Website"
>
<section title="My Homepage Section Title">
<![CDATA[
<p>Here is lots of text, that I want to appear on the home page.</p>
]]>
</section>
<section title="Another Homepage Section Title">
<![CDATA[
<p>This is a second section I want to appear on my home page.</p>
]]>
</section>
</screen>
</site>
We have called the root element of the XML "site", and this will represent the website in total. We could decide to render the site using java swing, or some other non-web based protocol. There's nothing in the site model that is specific to web sites, or says whether we will use HTML, XHTML, jsp or asp pages. That's all down to the architecture, which will go into the templates.
Within the site, there's a logo, a cross-site menu with some menu items and a couple of screens, with some text in them split by headed sections. Not exactly huge, but it should be enough to give you an understanding of the basics of using the model to generate a set of output files.
The Project
Let's decide that we're going to generate a set of web pages in HTML. First we need to set up the project in the same way as we did in the first tutorial. I'm going to put a sub-directory under control for screen. We know we want to generate one html page for each specified screen, so we are likely to want a template in screen that generates our html page.
The other thing we'll have is an area for static output that we just don't want to generate. The logo and a css style sheet can go in there.
So the directory structure will look like
website website\control\screen website\specification website\static\css website\static\images
I'll copy over the build.xml from tutorial1 and make a couple of changes to copy over static files. This is just adding an ant copy task.
<copy todir="${outputDir}"
failonerror="false">
<fileset dir="${staticDir}"/>
</copy>
which I hope doesn't need further commentary, and an ant property staticDir to define where the static directory is.
The Transform
The simplest way to create a transform is to write something that's a bit like what you are aiming for, turn it into a template, and then apply it many times. Here we only want to apply it to two screens, but I'm sure you get the drift. This will be the screen.vm template that will go into the screen template sub-directory.
So the output html home page might look like:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>My Site - Welcome to the Home Page</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<link rel="stylesheet" type="text/css" media="all" href="./css/mysite.css"/>
</head>
<body>
<div id="header">
<div id="logo">
<a href="index.html"><img src="image/myLogo.jpg" alt="my logo" /></a>
</div> <!-- close logo -->
<div class="clear"></div>
<div id="top_menu">
<ul>
<li>
<a href="./index.html">Home</a>
</li>
<li>
<a href="./faq.html">FAQ</a>
</li>
<li>
<a href="./contact.html">Contact Us</a>
</li>
</ul>
</div> <!-- close top_menu -->
</div> <!-- close header -->
<div id="main_container">
<div id="title">
<span>Welcome to the Home Page</span>
</div>
<div id="body_column">
<div class="section"><h2>My Homepage Section Title</h2>
<p>Here is lots of text I want to appear on the home page.</p>
</div>
<div class="section"><h2>Another Homepage Section Title</h2>
<p>This is the second section I want to appear on the home page.</p>
</div>
</div><!-- close body_column -->
</div> <!-- close main container -->
</body>
</html>
We can apply a fairly formulaic way of turning this into a template.
- Lots of this page will be common to all pages, so let's copy the lot over to the template and edit that.
- Look for bits that will change and ask what do they depend on. Well the first thing that's going to change will be the title in the header. The first part of the content is the site name, and the second part is the page title and it's separated by a hyphen.
So we use
<title>${parent.name} - ${title}</title>
We saw the use of $this in the last tutorial to refer to the current object. In that case it was the root element object, in this case it will refer the current screen object. Properties of the object can be referred to using ${this.propertyname} or ${propertyname}. In fact the braces are only needed when the ending of the name is ambiguous and we could just use $propertyname, but I suggest keeping the braces until you get used to where you can leave them out. So ${title} above will substitute the value of the page title.
In much the same way as you can access $this, you can access $parent. This refers to the current object's parent object. For a screen object the parent object is site. - Look for repeating groups of things. What are they based on? Well the menu at the top is based on the menu items of the site-menu, and the sections of text at the bottom depend on the sections in the specification. We shouldn't be surprised that there's such a close correspondence between the specification and the final output, as style sheets are supposed to do in a limited way for web pages, what code generation does for everything: that is separate the presentation from the meaning.
Just as we can access properties as text values by using $this.property, we can access child objects as lists by using $this.childList. We can then cycle through them using #foreach just like we did in the first tutorial. So
#foreach ($mySection in $sectionList) <div class="section"><h2>${mySection.title}</h2> ${section.text} </div> #end
JeeWiz will automatically make the list sectionList available to you because there are child elements in the specification of type section. Hyphenated element names become camelcase so site-menu gives siteMenuList. - Look for options and variations. For some page types we might want to show some thing one way and for others another. We can use the #if to decide what to do. For example if I only wanted to show the logo on the home page I might use
#if ($name == "index") div id="logo"> <a href="index.html"> <img src="image/${parent.logo.image}" alt="${parent.logo.description}" /> </a> </div> <!-- close logo --> #end
But I think I'll leave it on all pages for the moment.
So the template now looks like
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>${parent.name} - ${title}</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<link rel="stylesheet" type="text/css" media="all" href="./css/mysite.css"/>
</head>
<body>
<div id="header">
<div id="logo">
<a href="index.html">
<img src="image/${parent.logo.image}" alt="${parent.logo.description}" />
</a>
</div> <!-- close logo -->
<div class="clear"></div>
#foreach ($siteMenu in $parent.siteMenuList)
<div id="top_menu">
<ul>
#foreach ($item in $siteMenu.menuItemList)
<li>
<a href="./${item.page}.html">${item.linkCaption}</a>
</li>
#end
</ul>
</div> <!-- close top_menu -->
#end
</div> <!-- close header -->
<div id="main_container">
<div id="title">
<span>${title}</span>
</div>
<div id="body_column">
#foreach ($mySection in $sectionList)
<div class="section"><h2>${mySection.title}</h2>
${mySection.text}
</div>
#end
</div><!-- close body_column -->
</div> <!-- close main container -->
</body>
</html>
There's an oddity in this. We loop around the site menu list, when it's obvious that there is only one site menu. We could use the java get method and say
#set ($siteMenu = $parent.siteMenuList.get(0))
checking first for its existence so it doesn't throw an error if it's not in the spec. But that's not the real answer, which will have to wait for the next tutorial when we look at meta-models.
There's a third way to get at XML information on an element. Attributes become String object properties, like ${this.title}, child elements become Lists, such as ${parent.siteMenuList}, and simple text between the opening and closing tags of an element becomes a property called text. So we can access ${mySection.text}.
But there's a potential problem. If we want to pass xhtml markup in our text straight through to the output, it will have element tags in it. So how do we stop JeeWiz parsing this and turning into child lists? Then we'd have to process each element by turning it back into itself! We could change the specification to use an attribute
text = "<p>Here's all the text of my section</p>"
XML gives us the syntax to tell us that the character data is not to be parsed further. We use <![CDATA[ and ]]> to bracket the text and the JeeWiz parser will leave it as text. That's why this was put into the specification to allow for markup. If you want it to show as angle brackets in HTML text, you still need to convert to > etc, but that's not down to JeeWiz!
I Want to Call it a Page!
It's all very well trying to be neutral about how we write a specification, after all, I already compromised on allowing html markup in my specification text. So what if I want to call a screen a page instead? Or if I want to call it a screen and someone else wants to call it a page? The template sub directories are found by matching the name of the elements and the object properties and lists referred to in the template language are made that way too. But it doesn't mean editing things from scratch.
JeeWiz allows for a specification conversion while creating the object tree. We have already met the component.properties file, which lets you set up properties accessible by a single element type, we also have system.properties placed in the template directory that can hold properties for all element types. It also allows us to massage the specification using convert() commands.
convert(page) = screen
tells JeeWiz to use the screen element name whenever it encounters page.
You can also prune the specification using
convert(prune-me)=*
which will ignore all references to prune-me and its children, or
convert(promote-children)=-
which will ignore the element and move all its children to become children of the parent element.
This sort of facility is very useful if you are reading specification in a complicated format over which you have no control, such as XMI.
Summary
You can you specify an XML document as a model specification, or a directory containing one or more XML documents. In latter case, the top level element of a file called assembly.xml will be considered the overall top level element and everything else will be merged under it.
JeeWiz constructs an object tree from the XML specification.
To construct the output files, JeeWiz walks the object tree and for each object looks for a template subdirectory of the same name as the object type. If it finds a build.xml file in the subdirectory it uses it to create the output from the template.
The creation of a template file can be formulaic if you have a concrete example of the sort of thing you want to generate. It's often worth writing a concrete example first before attempting to write the template.
If the type of the object in the specification doesn't match the template you want it to run, you can define a synonym using convert in the system.properties file.
Exercises
The results of the tutorial will be found in %jwhome%\examples\tutorial\02website. If you have an older release of JeeWiz without the tutorial, you may also download it separately from here.
- Copy this to your own area and build it. View the index file in a browser.
- Alter the specification so that screen elements are called page. Then create a text file called system.properties in 02website\control and put the relevant control command in to convert page references and rebuild.
- The faq page has a line of text in a section. Mark up the text specification so that the words "logical semantic" are emphasised (using <em>). Regenerate and check the results.
- Add a third page named "contact". Regenerate. Make sure that the link on the index page to your new page works.
