Chapter 53. Templated Generation

Generate output by handwriting an output file and placing template callouts to generate variable portions.

image

53.1 How It Works

The basic idea behind Templated Generation is to write the output file you desire, inserting callouts for all the bits that vary. You then use a template processor with the template file and a context that can fill the callouts to populate the real output file.

Templated Generation is a very old technique, familiar to anyone who has used mail-merge facilities in a word processor. Templated Generation is very common in web development, as many websites with dynamic content use Templated Generation. In those forms, the entire document is a template, but Templated Generation also works in smaller contexts too. The old faithful printf function in C is an example of using Templated Generation to print out a single string at a time. In the context of code generation, I usually use Templated Generation for the cases where the whole output document is a template, but printf reminds us that Templated Generation and Transformer Generation can be very intermixed. Textual macro processors, another old standby in software development, are another form of Templated Generation.

With Templated Generation, there are three main components: templating engine, template, and context. The template is is the source text of the output file, with the dynamic parts represented by callouts. The callouts involve references to the context which will populate the dynamic elements when the generation takes place. The context therefore acts as a source for dynamic data—essentially, the data model for the template generation. The context may be a simple data structure or a more complex programmatic context; different templating tools use different forms of context. The templating engine is the tool that brings template and context together to produce the output. A controlling program will execute the templating program with a particular context and template to produce an output file, and may run the same template with multiple contexts to produce multiple outputs.

The most general form of template processors allow arbitrary host code expressions to be placed in the callouts. This is a common mechanism used by tools like JSP and ASP. Like any form of Foreign Code, it needs to be used with care, otherwise the structure of the host code can overwhelm the template. I strongly recommend that, if you have a template processor that embeds arbitrary host code, you confine yourself to simple function calls within the callouts, preferably using an Embedment Helper.

Because it’s common for template files to get thoroughly messed up due to too much host code, many template processors don’t allow arbitrary host code in the callouts. Such tools provide a specific templating language to be used in the callouts instead of host code. This templating language is usually quite restricted to encourage simpler callouts and preserve the clarity of the template structure. The simplest kind of templating language treats the context as a map and provides expressions to look up values in that map and insert them into the output. While this mechanism is sufficient for simple templates, there are common cases where you need more.

A common driver for more complex templating is the need to generate output for items in a collection. This usually requires some kind of iterative construct, such as a loop. Conditional generation is another common need, where different template output is needed depending upon a value in the context. Often, you find duplication of chunks of template source, which suggests the need for some kind of subroutine mechanism inside the template language itself.

I’m not going to delve into the different ways that various templating systems handle these cases, although that is an interesting diversion. My general advice here is to be as minimalist as possible, since the strength of Templated Generation is directly proportional to how easy it is to visualize the output file by looking at the template.

53.2 When to Use It

The great strength of Templated Generation is that you can look at the template file and easily understand what the generated output will look like. This is most useful when there is quite a lot of static content in the output while the dynamic content is occasional and simple.

So the first indication that you may want to use Templated Generation is a lot of static content in the generated file. The greater the proportion of static content, the more likely that it will be easier to use Templated Generation. The second thing to consider is the complexity of the dynamic content you need to generate. The more you use iterations, conditionals, and advanced templating language features, the harder it is to comprehend what the output will look like from the template file. When this happens, you should consider Transformer Generation instead.

53.3 Generating the Secret Panel State Machine with Nested Conditionals (Velocity and Java generating C)

Generating code for nested conditionals in a state machine is a good case where the static output is relatively large and the dynamic part is fairly simple—all good indications for Templated Generation. For this example, I’ll generate the code that I discussed in the example for Model Ignorant Generation. To give you a sense of what we want, here is the entire output file:

image

image

image

image

The engine I’m using here is Apache Velocity, which is a common and easy to understand templating engine available for Java and C#.

I can look at this overall file as segments of dynamic content that need to be generated. Each segment is driven by a collection of elements that I can iterate through to generate the code for that segment.

I’ll begin by looking at how I generate the event definitions, such as #define EVENT_doorClosed "D1CL". If you follow how this works, the rest pretty much falls into place.

I’ll start with the code in the template.

image

Unfortunately, one of the confusions here is that both the C preprocessor (itself a form of Templated Generation) and Velocity both use “#” to indicate template commands. #foreach is a command for Velocity while #define is a command for the C preprocessor. Velocity will ignore any commands it doesn’t recognize, so it will just treat #define as text.

#foreach is a Velocity directive to iterate over a collection. It takes each element from $helper.events in turn and runs its body with $e set to that element. In other words, this is a typical for-each style construct.

$helper.events is a reference to the context of the template. I’m using an Embedment Helper and have thus placed just the helper, in this case an instance of SwitchHelper, into the Velocity context. The helper is initialized with a state machine, and the events property provides access to it.

image

Each event is an object from the Semantic Model. As a result, I can use the code property directly. However, creating a constant to reference in the code is a little more work; for this, I put some code in the helper.

image

Of course there’s no absolute need to use a constant here; I could just use the event code itself. I generate the constant because I prefer even my generated code to be readable.

As is usually the case, the commands use exactly the same mechanism as the events, so I’ll leave that code to your imagination.

To generate the states, I need to sort out an integer constant.

image

Some readers may be uncomfortable with me generating and sorting a list of states every time I need an ID. Rest assured that if this was a performance issue, I’d cache the sorted list, but since it isn’t I don’t.

With all the declarations generated, I can now generate the conditionals. First, the outer conditional switches on the current state.

image

The inner conditionals switch on the input event. I’ve broken these into separate functions.

image

To get the transitions for each state, I need both the transitions defined in the Semantic Model and the reset event transitions.

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.97.85