Code Generation Tutorial Part 1 - Scripting Project

Objective

We are going to have a look at how JeeWiz can be used to create an output file using a template varied by setting input properties. It's unlikely that you would start using JeeWiz if this were the level of code generation you needed, but if you already use JeeWiz for more sophisticated tasks, it can also be convenient to use it for basic scripting. More importantly, scripting forms a platform for learning to use JeeWiz.




Directory Structure

JeeWiz projects are usually placed in their own directories and are built using ant scripts. If the project is created in Eclipse, the directory structure will be created for you as part of the project, but we are going to create the first few projects using XML by hand. Once you've built a project, it's simple enough to copy and paste the structure from a similar project.

We'll call the top level directory the project directory. We'll create a template directory below that (which is normally called control) and a specification subdirectory. If we were using a UML intermediate XMI file we would typically add a subdirectories to handle that, but we'll just work in XML for the first few examples. We'll build into another output sub-directory structure, and the JeeWiz ant project will create that during the generation. We don't actually want to use any real specification, but JeeWiz expects something and it needs to know which template to apply. So we'll create an XML specification file called assembly.xml with one element <root/>.

That means we can create a directory under the template directory called root and JeeWiz will use it by default to render output.

So we start with the directory structure


scripting
scripting/specification
scripting/control/root
		

There's nothing special about the element name root. We could call it fred if you prefer, but the subdirectory would have to be named to match.

Ant Builds

JeeWiz uses ant to create directories, move files about, set the generation off, compile and deploy the results if necessary. The main JeeWiz entry points called jwrun and jwcall are special ant files. If you aren't used to ant, it's worth taking a look at a simplified build file. By default these are called build.xml, and JeeWiz makes extensive use of them.

The top level element is the project. We'll call this Scripting.

<project name="Scripting" default="build" >

The two main elements inside a project are properties and targets, the targets defining what is done when ant is run. Ant also supports tasks, which is when an ant target calls another ant sub-project in a different file. The default attribute in project is the target that will be run if none is specified by the call to ant. The base directory can be allowed to default to our top level or we can specify it in the project call by adding basedir = ".". We'll let this default and we can override it if we want in a properties file.

We can set up some properties to say where JeWiz home is and where we intend to send the output of the generation.

<property name="jwhome" location="${ant.home}/../../"/>
<property name="assemblyDir" location="${basedir}/assembly" />
<property name="templateDir" location="${basedir}/template" />
<property name="outputDir" location="${basedir}/output" />

${ant.home} is a property set by the ant launcher to the ant home directory, which we have placed under the jeewiz home directory. So we can use that to find Jeewiz home.

Calling JeeWiz

Next we need to create the target that calls a JeeWiz ant task.


<target     name="build">
   <delete  dir="${outputDir} quiet="true" />
   <ant     name = "${jwhome}/bin/jwcall" />
</target>

That is all that's necessary for the build file (apart from closing the project element).

When the buildfile is run in ant, the default target, build, deletes the old generated output and calls a JeeWiz build, passing in the required configuration variables assemblyDir and templateDir, and also the outputDir which we will use as part of the build.

When the JeeWiz job is called, it will read in the specification, which is just the root element, it will fail to find a special meta-model class called root and will use a default class to create a java object, which will be the whole input tree. There are no patterns to run so the next step is traverse the tree looking for templates to render. If it finds a build.xml file in the root template subdirectory, it will fire its ant build target.

Generating Output

So we want to create a template in scripting/control/root to handle the root element. I'll call it root.vm. In this case the name doesn't have to match the element and could be anything specified in the build.xml, which we also have to create.


<project           name="build"  default="build">
   <target         name="build">
      <mkdir       dir="${outputDir}" />
      <jwVelocity  template="root.vm"
                   file="${outputDir}/output.txt" />
      />
  </target>
</project>

Jeewiz will call the build target which will create the output directory and use the root.vm file as a template to create the output.txt file. The jwVelocity ant task gives access to all the JeeWiz scripting functionality and velocity extensions.

Another place we can take properties from is a file called component.properties in the template sub directory.

Running the project

This project exists in jeewiz\examples\tutorial\01scripting. If you start up a command window (Start run cmd), change to this directory and type


ant

JeeWiz will build the output.txt file. Have a look through the root.vm template file which will have some explanatory comments and the component.properties file.

We can see there's an optional section depending whether a variable called useColours is set, but it's not set in either the component.properties file or the buildfile. We can also set it via the command line call to ant.


ant -DuseColours=true

This will alter the output of the generated template.

Scripting Shortcut

This probably seems like a lot of hard work to use JeeWiz as a scripting language (and it is), but that's not what JeeWiz is primarily for. If you only want to work at this level, you might be better off using a purpose-built scripting language such as Perl or Python. This first tutorial is primarily to give you a feel for how JeeWiz projects are set up at a base level. Occasionally you can find that you'd like to use JeeWiz for a quick scripting transformation and there is a shortcut using the jwScript batch file. Call


jwscript myTemplate.txt param1 param2

For details see the Architect's Guide.

A Bit More about Variables

Velocity variables are backed by java ones, and using #set on a new variable will normally create a local String or int variable.

#set ($status = "live")
#set ($count = 1)

This will depend on the expression on the right hand side. The helper method getNewClassInstance can be used to get a variable of any java type (more about helper methods later), and there are special variables available for $booleanTrue, $booleanFalse and $null.

In java there is a problem comparing String values, and you have to use code such as if (string1.equals(string2)). In script you don't need to do that and you can just write

#if ($string1 == $string2)

In most cases testing #if ($variable) will test whether $variable has been set or if it's null, so any value will be treated as true. The exception is for booleans where the test will fail for a set false value.

When a variable is rendered, it converts to a String form. If the variable doesn't exist the parse substitutes the name of the variable and throws a warning. So if you see $variable in your output, you know something went wrong. Sometimes we want to output a value if its there and an empty String if it hasn't been set. That's achieved by using an exclamation mark after the dollar sign. Eg. $!variable. So if $variable was null ($!variable == "") would be true.

Don't confuse that syntax with !$variable where the exclamation mark before the dollar sign negates the variable in an expression.

Summary

A JeeWiz project can be set up to run as an ant build by calling one of the two special tasks jwcall or jwrun. Projects can also be created and run inside IDEs such as Eclipse.

Jeewiz uses a scripting language extended from Velocity, with java-backed variables and a simple syntax.

Variables are prefaced by a dollar sign, and placed inside curly braces where necessary. Methods on the variables can be accessed using dot notation.

When a #if directive tests a variable (rather than an explicit expression), it normally evaluates whether the variable is set at all (not null). The exception is for boolean variables where it tests for set true.

Variables can be set in ant or passed into ant from the command line. When using jwcall these variables are made available to JeeWiz automatically.

JeeWiz expects a specification to tell it which templates to use, the name of the element in the specification being the same as the template subdirectory name checked.

A build.xml in the template subdirectory is used to call a jwVelocity task that transforms the template into an output file.

Exercises

If you haven't already, start up a command window (Start-run-"cmd" or use the right click menu in Windows explorer for XP+) and navigate to %jwhome%\examples\tutorial\01scripting\ . If you have an older release of JeeWiz without the tutorial, you may also download it separately from here.

Then test that you can build using ant. If you have problems, check that your environment variables are set up as in the installation guide (or see the download page) and that you have the right variables included in the path.

When you have this working, create a new examples directory of your own, and create your own project by copying the project, specification and template directories and renaming them.

  • Change the element "root" in the assembly.xml to something else, such as "top". Make the appropriate changes to the rest of the build to match, change directories in the command window and run an ant build.
  • Add a value to the new element for example <top myvalue="hi there"/>. Make this value come out in your output.txt file using dot notation. Remember that the current object is referred to in the script as $this.
  • Look for the helper method list in the Architect's Guide, in the Templates and Velocity Features section. Find one that outputs logging information to the console and use it to send the name of the base directory to the console