9.11. Using a Complex Scripting Engine

Problem

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.

Solution

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} 
${course.name}.  

Class:         ${course.name}
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 >
    ${exam.name}, 
    ${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: 
" + 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!

Discussion

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:

Formatting dates and numbers

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.”

Comparing dates and numbers

FreeMarker can compare dates and numbers, both integer and floating point.

Macros with named parameters

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

Tip

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 Jakarta Velocity; they even have a feature comparison page (http://www.freemarker.org/fmVsVel.html).

See Also

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 Jakarta 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.

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

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