I’ve been using Xpand (in combination with Xtend) for some four years now and it still is my favorite (target language-agnostic) templating engine. As all programmers, I picked up a few habits which I’ll conveniently rebrand as “tips and tricks” 🙂
Separating templates from logic
Xpand is closely tied with Xtend: not only do they share an expression sub language, but they’re also each other’s perfect buddies in crime since Xtend (as a separate language) allows you to factor out re-used or intricate expressions into a separate file, so the Xpand template only needs to call (hopefully aptly-named) functions in a separate Xtend file. As a bonus, this approach allows you to use the cached keyword so that commonly used, but expensive expressions have to be evaluated only once as well as offering a more convenient way of documenting functionality (see also XtendTools).
Xtext ships with a number of standard libraries which can be quite helpful, especially the io library. These libraries are all contained in the org.eclipse.xtend.util.stdlib plugin so be sure to add this one to the plugin dependencies (in the META-INF/manifest.mf file). I’ll highlight a few of the libraries (in alphabetical order):
- counter.ext: provides counters inside templates (if you really need these -most of the time you don’t and you simply didn’t think hard enough about it, yet ;));
- crossref.ext: getReferencingObjects(EObject eObject) compiles a list of all objects (in the current Resource) referencing eObject;
- elementprops.ext: provides access to System properties;
- io.ext: provides console logging;
- naming.ext: provides common naming, such as computing a qualified name from the containment hierarchy;
- properties.ext: provides access to properties injected into the workflow using the org.eclipse.xtend.util.stdlib.PropertiesReader workflow component;
- uid.ext: provides common UID functionality/generation.
I’d advise anyone to go over these libraries by looking at the .ext files, checking out the (somewhat sparse) documentation and do some ‘finger exercises’ using them.
Polymorphic dispatch and the use of a sentinel
As most things in the Xtext universe, Xpand features polymorphic dispatch, i.e.: in case of an overloaded DEFINE block, Xpand decides which one to call based on the actual (runtime) type of the arguments. This is extremely useful but also presents a slight inconvenience in case of a type hierarchy (as opposed to a number of types having only (E)Object as common super type). As an example, consider the hierarchy where Apple and Orange are types having Fruit as common, direct super type. Now, when you write DEFINE blocks for Apple and Orange with common name ‘squeeze‘ and you call squeeze from someplace you’re dealing with Fruits, the Xpand editor will complain that it “Couldn’t find definition squeeze for type metamodel::Fruit”. The Xpand editor is not able to see that you’ve covered all essential cases, even if Fruit is defined as abstract. The only way to deal with this, is to write a third DEFINE block which acts as a sentinel to cover the Fruit case (har, har).
However, this sentinel can be put to good use by mixing in some fail-safe in case your meta model changes. E.g., if you add Banana to the type hierarchy of Fruit without writing a separate DEFINE block for it, the sentinel block will be executed but obviously it cannot produce meaningful output on its own. That’s why I tend to put in some error logging in the DEFINE block, like so:
«DEFINE squeeze FOR Type» «( "no (specific) DEFINE block 'squeeze' defined for type " + this.metaType.name ).error()» «ENDDEFINE»
Here, I use the error function from the io.ext library mentioned above which you include as follows:
Now, you’ll get a meaningful error message in the console when executing the template, indicating that Banana is missing a (specific) DEFINE block. (Of course, you could just as well factor the inner expression of the sentinel block out into a separate Xtend file.)
Readable template versus readable output
One of the choices you have when creating a template is whether to make the template readable exclusive-or to make the output readable, i.e. nicely formatted. My advice: choose the former (readable template), especially if there’s a beautifier for the output format. For a lot of the more common output formats, such a Java and XML, Xpand beautifiers have been written. Have a look here (navigate to the ‘PostProcessor’ section) to learn more about them and how to configure these. You might not be an instant fan of the beautification offered out of the box, but it generally beats the trouble of getting the template output nicely formatted. Moreover, this choice fits my earlier reasoning the meta software (such as code generators and templates) is software as well, so the readability argument is very much in scope here.
Getting the template output nicely formatted is frankly a pain in the buttocks, for a number of reasons: (1) Xpand constructs often seem to add whitespace unintentionally and (2) it’s hard to get indentation correct with “recursive content”. In fact, the Xpand constructs do not add whitespace: it’s just that everything between ‘»’ and ‘«’ (while inside a FILE statement of course) is part of the template text and ends up in the output. This means that something like
«FOREACH entities AS e» entity «e.name»; «ENDFOREACH»
will produce a newline in front of each entity in the output because of the newline after the first ‘»’. Xpand allows you to add a minus sign just before that ‘»’ which has the effect that the whole uninterrupted sequence of whitespace characters (i.e., everything the next non-whitespace character) is gobbled up so it doesn’t produce anything in the output. This might help a lot getting unwanted whitespace out of the way, but it certainly doesn’t help with producing the correct indentation so the ‘-‘ is a two-edged sword.
Again, in general I’d go for a readable template unless there are very pressing reasons (such as the lack of a beautifier) to make the output look nice as-is. In some cases, it might even be worthwhile to combine a readable template with a custom-made parser for the target language and a (separate) serializer/pretty-printer/beautifier for that target language. (In fact, this is how the XML beautifier arose as part of openArchitectureWare.) This adds a step to the pipeline, but it also adds some validation of the templates’ output. Xtext could very well be a perfect fit for this case, as you quite probably can even re-using the existing meta model.