9.8. Using Macros in a Templating Engine

Problem

You need to reuse portions of a template to standardize the display of common elements such as an address or a name.

Solution

Use Velocity Macro definitions to reuse logic to print out both names and addresses. Velocity macros are like subroutines that take a set of parameters and perform common tasks. In the following Velocity template, two macros, #name and #address, handle the printing of names and addresses:

#set( $volunteer = $appointment.volunteer )
#set( $location = $appointment.location )
#set( $org = $appointment.organization )

## Define the "name" macro
#macro( name $object )$!object.firstName $!object.lastName#end

## Define the "address" macro
#macro( address $object )
$!object.address.street1
$!object.address.street2
$!object.address.city, $!object.address.state $!object.address.zipcode
#end

#name( $volunteer ),

Thank you for volunteering to help serve food at the $location.name next 
week.  This email is a reminder that you are scheduled to help out from 
$appointment.startTime to $appointment.endTime on $appointment.date.  
The address of the shelter is:

#address( $location )    

If you need directions to the shelter click the following URL:

    ${org.baseUrl}directions?location=${location.id}

Also, if you are unable to help out on $appointment.date, please let us know by 
sending an email to ${org.email} or by filling out the form at this URL:

    ${org.baseUrl}planschange?appointment=${appointment.id}

Thanks again,

#name( $org.president )

#address( $org )

In the following code, the template shown previously is loaded from a classpath resource organize.vm, and an Appointment object is placed in a VelocityContext:

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;

// Create and initialize a VelocityEngine setting a configuration property
VelocityEngine vEngine = new VelocityEngine( );
vEnging.setProperty( RuntimeConstants.VM_CONTEXT_LOCALSCOPE, Boolean.TRUE );
vEngine.init( );

// Create a test Appointment
Appointment appointment = testAppointment( );

// Create a Velocity Context and give it the appointment 
VelocityContext context = new VelocityContext( );
context.put("appointment", appointment);

// Prepare a StringWriter that will hold the contents of
// our template merge
StringWriter writer = new StringWriter( );

// Get a stream to read in our velocity template.  The
// organize.vm file is loaded from the classpath and is stored
// in the same package as the current class.
InputStream templateStream = getClass( ).getResourceAsStream("organize.vm");
Reader reader = new InputStreamReader( templateStream );

// Evaluate the template
vEngine.evaluate(context, writer, "test", reader);
        
// Print out the results of the template evaluation
System.out.println( "organize: " + writer.toString( ) );

The template is merged with a VelocityContext, and the following output is produced:

               John S.,

Thank you for volunteering to help serve food at the Boston Homeless 
Veterans Shelter next week.  This email is a reminder that you are 
scheduled to help out from 9:00 AM to 2:00 PM on Monday, September 
12, 2003.  The address of the shelter is:

    17 Court Street
    Boston, MA 01260

If you need directions to the shelter click the following URL:

    http://www.organize.com/directions?location=2342

Also, if you are unable to help out on September 12th, please let us 
know by sending an email to [email protected] 
or by filling out the form at this URL:

    http://www.organize.com/planschange?appointment=29932422

Thanks again,

Brishen R.
201 N. 2nd Street
Jersey City, NJ 20213

Discussion

A macro definition is started with the #macro directive and ended with #end; the same macro is invoked by calling #<macro_name>( <parameters> ). Velocity macros must be defined before they are referenced, using the following syntax:

#macro(<name> <arguments>)
    <Macro Body>
#end

Macro parameters are not typed as are method parameters in Java; there is no mechanism to check that an Address object is passed to the #address macro, throwing an exception if an inappropriate object is encountered. To successfully render this Velocity template, verify that an Address is sent to the #address macro and a Person is sent to the #name macro.

In the previous example, an instance of VelocityEngine is created and the RuntimeConstants.VM_CONTEXT_LOCALSCOPE property is set to true. This property corresponds to the velocimacro.context.localscope, which controls the scope of references created by #set directives within macros. When this configuration property is set to true, references created in the body of a macro are local to that macro.

The Velocity template in the Solution expects a single reference $appointment to an Appointment bean. Each Appointment has a volunteer property of type Person, and every Organization has a president property of type Person. These Person objects, ${appointment.volunteer} and ${appointment.organization.president}, are passed to the #name macro that prints out the first and last name. Two Address objects, ${appointment.location.address} and ${appointment.organization.address}, are passed to the #address macro that prints a standard U.S. mailing address.

A macro can contain any directive used in Velocity; the following macro uses nested directives to print out a list of numbers in HTML. #numberList allows you to specify a range with $low and $high; values in $numbers within this range will be printed bold:

#macro( numberList $numbers $low $high )
    <ul>
     #foreach( $number in $numbers )
       #if( ($number > $low) && ($number < $high) ) 
           <li><b>$number</b> - In Range!</li>
       #else
         <li>$number</li> - Out of Range!</li>
        #end
     #end
   </ul>
#end

The macro defined above would be called by the following Velocity template. Note the presence of comments, which are preceded by two hashes (##):

#set( $squares = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] )

## Print out a list of numbers highlighting numbers
## between 25 and 75
#numberList( $squares, 25, 75 )

See Also

If your system has a large number of Velocity templates, you can create a set of files to hold common macros, which will be made available to every Velocity template using the velocimacro.library property. For more information, see the Velocity User Guide (http://jakarta.apache.org/velocity/user-guide.html#Velocimacros).

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

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