You need to reuse portions of a template to standardize the display of common elements such as an address or a name.
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=org.codehaus.cjcook:cjcook-content:jar:0.19 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=org.codehaus.cjcook:cjcook-content:jar:0.19 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 organize@helpout.com 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
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 )
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://velocity.apache.org/engine/releases/velocity-1.6.1/user-guide.html#Velocimacros).