JeeWiz Home  
The Model-Driven System Builder

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


8.5 #divert/#revert

Occasionally when writing templates and patterns, you may find it convenient to create some text as part of processing the script, and then use that text later.

For example, this commonly happens when creating XML documents, where an outer element is only required if there are nested elements. In this case, it makes sense to process the (possibly) nested elements first and then, if they produced any output, to create the outer element and include the output from processing the nested elements.

To assist with this, JeeWiz provides the #divert() directive to temporarily divert the the output of the script to a Velocity reference or a file. The reference will typicallly be a local variable - e.g. $phase1 - although any other form of reference is allowed.

There is a matching #revert directive, which terminates the current #divert() directive.

 8.5.1  Syntax
 8.5.2  Operation
 8.5.3  'divert'ed References

8.5.1  Syntax
#divert() requires a single argument which must be either a valid reference:
#divert( $foo )
#divert( $this.bar )
or a string literal
#divert( "$filePath" )
The parentheses are required to invoke the #divert() functionality.

#revert is called without an argument, so the parenthese can be omitted:
#revert

8.5.2  Operation
When a #divert() is encountered, output text from the templater or pattern does not go to the current output target (e.g. a file if a template is being run). Instead, it is diverted as specified in the parameter: for a reference, the output is set into the value of the reference as described below; for a string literal parameter, this is taken as the name of the output file to be written with the eventual result of the diverted text. In the reference argument format, the reference in the #divert() directive must be evaluated at some later point to get access to the diverted output. #divert() and #revert are executable statements and are independent of the flow of control structure. In other words, it is possible to write sequences like this:
#if( $doDivert )
    #divert( $divertTarget )
#end
...
#if( $doDivert )
    #revert
#end
...
#if( $doDivert )
    $divertTarget    #* include the diverted output *#
#end
When the last #divert() is #revert-ed, then the output of the script reverts to the original target (whatever file or property it was being directed to, as described in the <jwVelocity> task).

Any whitespace on the end of the diverted text is removed - but not whitespace at the start of the text. This is done because #divert() is a line-oriented directive and the output will tend to also be output as a line. This would cause an extra blank line to be introduced at the end of the text. The removal of the trailing whitespace removes the extra blank line. This does mean that you should not use #divert() when the output has significant whitespace on the end.

The #divert() and #revert directives must be matched up. It is an error to execute the #revert directive if there is no extant #divert() directive. It is also an error to leave one or more #divert()-ed variables remaining at the end of the script. In the case of these errors, the engine throws an exception and terminates the build.

However, there is no restriction on #divert/#revert being outstanding when a #return is executed; returning immediately implies discarding all the #divert-ed streams.

It is possible to have a #divert() outstanding when a call to #parse is made. The output of the #parse script is added onto the current output, now diverting to the reference argument of the divert(), as normal for script evaluation.

As implied by the names of these directives, it is possible to have multiple #divert() directives in operation simultaneously.
8.5.3  'divert'ed References
When a #divert() directive is executed, a new StringWriter (java.io.StringWriter) is created to hold the forthcoming output of the script. In the reference argument format, the new StringWriter is immediately set into the reference; At this point the new StringWriter will be empty. In the string literal format, the StringWriter is held internally and is inaccessible.

Note that it is not allowed to use the same reference simultaneously in a stack of #divert() directives. In other words, it is an error to say
#divert( $divertTarget )
#divert( $divertTarget )
As the script is executed, the output is collected in the StringWriter. If you were to evaluate the reference during the script, it would change as script was executed to produce the output, so it is not recommended to reference the value as the diversion is executed.

When the matching #revert directive is executed, the value of the StringWriter is taken (i.e. as a Java String object); the StringWriter is then discarded and unreferenceable. The string value is then trimmed at the end. The reason for trimming the string at the end only is the same as for the trim feature on methods: when we build up an output stream in divert, it easiest to write method calls on separate lines - which normally leads to extra linefeeds on the end. The string is not trimmed at the front because that is easier to control and is often important to the layout of generated text.

(However, if the diverted output is effectively empty, the trimming algorithm - at the front - means that the output will be the 0-length (empty) string. See the example below for a use of this approach.) The reference will typically be used to include the text in a later output - e.g. '$divertTarget' in our example above.

The diversion spans calls to methods. This can be used in a typical sequence that tops-and-tails the output of a call, but only if it produces some output:
#method( getNavigationRule )
   #divert( $cases )
                  $self.findEventHandlerChildren( $self )
   #revert
   #if( $cases != "" ) ## $cases will have is now the trimmed string, not a StringWriter
                  ${i}
                     /${viewId}
                     $cases
                  ${u}
   #end
#end
In this method, a new StringWriter is created and assigned to $cases. Then the method to render all the event handlers in the children ('findEventHandlerChildren') is called. If this produced any non-whitespace output, it is topped and tailed with a navigation rule. Even though the event-handler processing proceeds through other methods, and probably uses other objects to build its output, the diversion to the variable $cases in the current context remains intact.

If you use #divert() inside a #foreach loop, you can use a single local variable as the reference in the #divert() directive. Any existing value (before the #foreach loop) is lost; as the #divert() is executed in each iteration, the local variable will be set to a StringWriter initially, then a String.  


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