You need to find a templating engine that supports number formatting, date formatting, and comparison of double values. In addition, you are looking for a templating engine that gives you more control over whitespace and line breaks.
Use FreeMarker, a templating engine with a large built-in feature set that includes support for date and number formatting and intelligent handling of whitespace. The following FreeMarker template creates a summary report for a college course:
<#assign student = enrollment.student >
<#assign course = enrollment.course >
<#assign exams = enrollment.exams >
<#-- This macro assigns a variable named final -->
<@final exams=exams/>
${student.firstName} ${student.lastName},
Here is a summary of your performance in ${course.dept} ${course.num}
Common Java Cookbook DocBook XML Content.
Class: Common Java Cookbook DocBook XML Content
Professor: ${course.professor}
Section: ${enrollment.section?string("000")}
Exam, Date, Score, Weight, Grade
-----------------------------------------------
<#list exams as exam>
<@compress single_line=true>
<#assign score = exam.score >
Common Java Cookbook DocBook XML Content,
${exam.date?date?string.short},
#{exam.score; m1M1},
${exam.weight},
<@letter score=score/>
</@compress>
</#list>
Final Grade: ${final; m1M1} <@letter score=final/>
Your final grade has been submitted to the Registrar.
<#macro final exams>
<#local num = 0>
<#local dem = 0>
<#list exams as exam>
<#local num = num + (exam.score * exam.weight)/>
<#local dem = dem + exam.weight>
</#list>
<#assign final = num / dem>
</#macro>
<#macro letter score>
<#if (score >= 90)> A
<#elseif (score >= 80)> B
<#elseif (score >= 70)> C
<#elseif (score >= 60)> D
<#else> F
</#if>
</#macro>To merge this template with data, populate a Map with named attributes and pass this
Map to the template.process() method. The following code creates a Configuration object that loads a FreeMarker
template, template.ftl, from the
classpath. An Enrollment object is
added to the root Map, and the output
of the template merge is written to a StringWriter:
import freemarker.template.Configuration;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
StringWriter writer = new StringWriter( );
// Create a Configuration object for FreeMarker
Configuration cfg = Configuration.getDefaultConfiguration( );
cfg.setTemplateLoader(new ClassTemplateLoader(getClass( )));
cfg.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
// The root Map serves as a Context for our template engine
Map root = new HashMap( );
root.put("enrollment", testEnrollment( ));
// A template is processed with a Map and output is sent to a Writer.
Template template = cfg.getTemplate("template.ftl");
template.process(root, writer);
System.out.println("output: \n" + writer.toString( ));The template is rendered, and the following output is printed to the console:
Stefan Winz, Here is a summary of your performance in ECON 201 Macroeconomics. Class: Macroeconomics Professor: Dr. Stephen H. Jones Section: 002 Exam, Date, Score, Weight, Grade ----------------------------------------------- T01, 01/10/03, 93.4, 1.00, A T02, 01/27/03, 85.5, 1.50, B Mid, 02/15/03, 98.0, 2.00, A+ T03, 03/31/03, 71.5, 1.00, C- T04, 04/10/03, 88.5, 1.50, B+ Fin, 05/05/03, 95.0, 4.00, A Final Grade: 91 A- Your final grade has been submitted to the Registrar. Have a great Summer!
In the template for this recipe, three objects are retrieved from
an Enrollment object: a course property, a student property, and a List of Exam objects. Three variables—course, student, and exam—are created with the <#assign> directive, <#assign variable
= expression >. Properties are referenced as they were
referenced in JEXL and Velocity; ${enrollment.student} is used to access the
student property on the enrollment. A student's final course grade is
calculated in a macro by calling <@final exams=exams/>. This macro assigns a global
template variable, final, which is
formatted to one decimal place with the expression ${final; m1M1}.
At first glance, a FreeMarker template looks very similar to a Velocity template, but there are several interesting features not available in Velocity:
Our date object was formatted with the expression ${exam.date?date?string.short}. ?date instructs the engine to take only
the day, month, and year portion of the date, and ?string.short tells FreeMarker to use
the locale's short-date format (12/31/04). You can also specify
your own date format using the same syntax you would use in
SimpleDateFormat. The
expression ${exam.date?string("MM-dd-yyyy
hh:mm:ss")} would output a string similar to "12-31-2004
04:23:22."
FreeMarker can compare dates and numbers, both integer and floating point.
Macros can be invoked with named parameters. For example,
the @letter macro can be
invoked with named parameters: <@letter team="Boston Celtics" score="34"></@letter>.
Here are some other interesting FreeMarker features not available in Velocity:
Namespaces for variables and macros
"Built-in" functions for basic types
Access to XML document objects
Improved looping
Local macro variables
Built-in XML and HTML escaping
Velocity has a very large user base, and it is the right tool for a simple job. FreeMarker has some very useful features "out of the box," while Velocity requires developers to install supporting utilities or write these "extensions" from scratch. Some developers will prefer a templating language that is simple by design, and others need a tool that is substantially more complex. There is a case to be made for simplicity. If you working on a large team, where you have content authors who need to create and maintain your templates, you may want to use a technology like Velocity that embraces simplicity. Open source communities benefit from healthy cross-pollination of ideas and competition, and FreeMarker was developed as an alternative to Apache Velocity; they even have a feature comparison page (http://www.freemarker.org/fmVsVel.html).
It is beyond the scope of this book to drill into the details of every FreeMarker feature listed in this recipe. If you are interested in learning more about FreeMarker, take a look at the online documentation (http://www.freemarker.org/docs/index.html).
If you are using Apache Velocity and wish to migrate your templates to FreeMarker, the FreeMarker team has written a utility, code named "US Cavalry," which will automatically translate your VTL templates to FTL templates. To obtain "US Cavalry," see http://www.freemarker.org/usCavalry.html.
