JeeWiz Home  
The Model-Driven System Builder

JeeWiz Architect's Guide
 
Contents  >   8.  Templates and Velocity Features
 


8.2 Miscellaneous Velocity Extensions

There are a number of JeeWiz enhancements to Velocity that apply to both templates and patterns. These are described in this and following sections. This section documents minor and miscellaneous features.

 8.2.1  Additional Values
 8.2.2  Indentation In Scripts
 8.2.3  Indentation In Generated Text
 8.2.4  Global Macros
 8.2.5  this, self and parent Cannot Be Set
 8.2.6  Setting Values In The Object Model
 8.2.7  Macro Variable Scope
 8.2.8  Velocity Gotchas
 8.2.9  Additional Methods

8.2.1  Additional Values
Each meta-class has the following values defined for it, which are available from Velocity scripts:
  • $this - the current model object.
  • $parent - the parent object. For example, the parent of an 'ejb-jar' will be its application; the parent of a parameter will be a method.
Each JeeWiz run has the following values defined at the top-most level, which are available to Velocity scripts running on any object (if not hidden by lower-level properties of the same name):
  • $topComponent - the top-level object itself (which, for this object, will be identical to $this)
  • the top-level objects from additional specifications, as described in Configuration Structure.

8.2.2  Indentation In Scripts
Velocity normally passes all whitespace through to the generated files, even on directive lines like
    #if( $x )   ## four spaces at beginning of line
This example will have the four extra spaces passed through to the generated text.

This makes it difficult to lay out scripts so that they are readable, because the result probably won't be: if you write scripts without indentation (to avoid extraneous whitespace in the generated text), the structure of the logic - both Velocity's and the target file's - is obscured, making it difficult to maintain.

JeeWiz therefore provides a facility to discard leading tabs. It is optional: to invoke it, you have to specify, in the build properties *.jwp file or one of the system.properties files that get processed at start of day:
discardLeadingTabs=true
(This is actually done for you in all the standard JeeWiz models, by a setting in the system.properties file.)

When this is set, leading tabs are discarded. A corollary is that any intended indentation - e.g. for a Java program - must start with at least one space before any other tabs.

The result of discarding leading tabs is that you can do use tabs to show the structure of both the Velocity script and the produced code. For example, see ejbJarXml.vm (the EJB Jar's script for producing 'ejb-jar.xml') for a script where this feature has proven invaluable. The JeeWiz standards for indentation, which are demonstrated in the ejbJarXml.vm example, are:
  1. Do required indentation in generated text files using spaces: reserve tabs for laying out the scripts.
  2. Indent 5 tabs for the left-hand side of the output file .
  3. Indent the created text as required from there, using spaces in the output file or the smart indentation feature described in the next section.
  4. Indent 0 tabs for outer-level directives in the Velocity script.
  5. Indent 1 tab for each level of nesting of Velocity directives - in #if, #else, #foreach and #macro.

8.2.3  Indentation In Generated Text
Indentation in the text generated by Velocity scripts may be important, particularly for produced code like Java source files.

This indentation is normally determined by the Velocity script, which can be laid out in an acceptable format. But when inserts are used, multi-line inserts can potentially disrupt the generated layout. For example, when there is a method whose implementation is present in the specification, the specifier does not know the indentation of the generated scripts - and nor should he or she.

To solve this problem, JeeWiz provides an 'indent' facility. This is triggered at the start by a special character (binary 1). This is followed by the indentation text to be added to the current indent. Normally the indent string is a number of spaces, but it can be any string - for example, it is also used to lay out Javadoc comments with ' * ' as the indent string. The indentation text is terminated by a second special character (binary 2). This causes the JeeWiz engine to add the additional indent on every line following until the matching end character (binary 3).

There are macros in the base model (see jeewiz\resources\base\control\system.properties) under the following names:
indentStartHeader=\u0001
indentStartBody=\u0002
indentEndBody=\u0003
The standard indentation pair is $i/$u - in other words, $i starts an indentated block and $u marks the last line. If these are the first things in the line, they affect the current line, otherwise the next line. These are sometimes used for laying out XML and HTML text, as in:

<tr>$i
   ...
$u</tr>
This indents the line after the '<tr>' and unindents on the '</tr>' line.

The standard brace indentation pair (for Java/C block start, end and indent) are named '$b' and '$e'.
8.2.4  Global Macros
Put global macros, to be used across a whole model, in VM_global_library.vm in the template directory (e.g. jeewiz/resources/weblogic6/control). [Local macros, which are in-line in a specific template file, are the other Velocity alternative, but they are not particularly useful in JeeWiz.]

JeeWiz builds an aggregate VM_global_library.vm for you. In the section on Finding Files, we show how other files are searched for down the model chain. But for VM_global_library.vm, the process is different. Instead of taking the first VM_global_library.vm found, these files from all models are aggregated together. The aggregation is done so that the most differentiated model takes precedence (e.g. any overriding model you construct will take precedence over weblogic6).
8.2.5  this, self and parent Cannot Be Set
$this, $self and $parent have specialised meanings in JeeWiz, so you cannot set these variables as local variables. For example, you cannot say
8.2.6  Setting Values In The Object Model
We noted above that when you use Velocity's #set directive, the resulting variable is always set in the local Velocity context.

In other words, there is an asymmetry here when the variable is the name of a property in the specification (one of the meta-classes): when we do substitution, the value comes from the object; when we set the value, it is set in the Velocity context.

This behaviour is deliberate: it means you can realistically reuse Velocity scripts in inherited models. For example, we could write a script in the business object model setting the variable $transaction, and a developer of the J2EE model could want to use the same name for a property on a J2EE meta-class. Were JeeWiz to automatically put #set values into model objects, there would be a problem here: the script would function differently depending on whether or not there was a 'transaction' property available in the specification. This is certainly undesirable, and almost certainly incorrect.

However, there are times when you want to set values in the model. The easiest way to do this is to use the '$this' variable. Remember that 'this' and 'parent' are automatically put into each object's extra properties at run time. So you could automatically invoke the setter via
#set ( $this.transaction="Required" )
This sets a value directly on the model object - locations 3 and 6 in the diagram at the start of this page.

You have to get the right object for this to work. You can also walk up and down the tree:
#set ( $parent.txAllowed="Required" )
#foreach( $method in $methodList )
     #set( $method.xxx=true )
#end
This works up the tree using $parent and down the tree using #foreach on a list of children. There is no typing mechanism in Velocity, so $parent or $method can be any sort of object.
8.2.7  Macro Variable Scope
Variables you set in Velocity macros are scoped. In other words, if you set a variable in a macro, the variable is created in the scope of the macro. The same applies to parameters passed into the macro: whatever value was passed in is copied into a variable created in the scope of the macro. Once a variable is set in a macro, subsequent references to it will find the variable in the macro's scope. If a variable is not set in a macro, then the normal search order applies, so the variables outside the macro can be accessed.
8.2.8  Velocity Gotchas
Null values in the RHS of a #set statement cause it to have no effect. e.g.

#set( $x=$maybeNull )
#if( $x )
    ...
#end
is wrong if '$maybeNull' is null: the evaluation of '$maybeNull' will get logged as an error in the velocity log and $x will be left unchanged - so the following #if will probably be incorrect.

Instead, you must evaluate the potentially null condition, and only if it is non-null set it into a variable:

#if( $maybeNull )
    #set( $x=$maybeNull )
    ...
#end

8.2.9  Additional Methods
All meta-classes derive from a base class which provides a basic set of methods. Therefore, you can use any variable referencing a model object to invoke these (but not just any Velocity variable - it must be a model object). The obvious one in a JeeWiz script is $this - because there is always a current object.

The additional methods are described in Helper Methods.  


Copyright (c) 2001-2008 New Technology/enterprise Ltd.