aberrant

6 posts

  Parts 1, 2 and 3 introduced StringTemplate through the use of simple SQL, HTML and plain text examples. This article switches gears and attempts to use StringTemplate to generate a more complicated text output: Java source code.

In “Effective Java: Second Edition” (go read it, no really go read it) Mr. Block puts forth the idea of using a “Builder” class to help construct complicated objects. His builder uses a “fluid interface” that also has the extra side effect of looking like named parameters.

Here is how you might use a builder class to create Planet objects.

Planet mercury = new PlanetBuilder().name("Mercury").mass(0.06f).build();
System.out.println(mercury);

Planet mars = new PlanetBuilder().name("mars").diameter(0.532f)
   .mass(0.11f).orbitalRadius(1.52f).orbitalPeriod(1.88f)
   .inclinationToSunsEquator(5.65f).orbitalEccentricity(0.093f)
   .rotationPeriod(1.03f).moons("Phobos","Deimos")
   .rings(false).atmosphere("CO2,N2").build();

System.out.println(mars);

The only issue I have with this type of API is that it requires a lot of maintenance. Every time a property is added to the target class another method must be added to the builder and that data needs to be factored into the build method. I believe that with StringTemplate and a dash of reflection we can generate a builder for any (Bean) class. Lets take a look at the Planet class to see what we are up against.

public class Planet {

  private String name;
   private float diameter;
   private float mass;
   private float orbitalRadius;
   private float orbitalPeriod;
   private float inclinationToSunsEquator;
   private float orbitalEccentricity;
   private float rotationPeriod;
   private String[] moons;
   private boolean rings;
   private String atmosphere;
   private List<String> notes;

...getters and setters ...
}

As you can see we have 12 private variables. In this case every private variable maps to a “property” with a getter and setter pair (not shown). Since this class implements getter and setters for it's properties we can use the java.beans.Introspector class to construct “property descriptors” that can give us all the information we need to generate a builder object.

But what information do we need?

  1. The package this generated class belongs to.
  2. Name for the builder class.
  3. The name of each property
  4. The type of each property.
  5. The setter method for each property.
  • Package Name:
     
    • The class needs to belong to some package. We can't really infer this from anywhere so it becomes a top level parameter to the template.
  • Builder Class Name:
     
    • In Effective Java a public static inner class called “Builder” is nested inside the target class. In this case we won't (or can't) modify the original source file. I have chosen simply to append “Builder” to the target classes name. In this case Planet → PlanetBuilder. The refection class “Class” has a getName() method which can provide us with the target classes name.
  • Property Names:
     
    • The PropertyDescriptor class also has a getName() method. This is the name part of a getter/setter pair. For example getMass + setMass define a property called “mass”. Each property has a unique name and we depend on this fact in our templates.
  • Property Types:
     
    • One would think that the Type of a property would be as easy to determine as the Name. Unfortunately primitives, arrays and generics make this slightly more complicated, yet not impossible. If you are interested in the details please see the source code at the end of this article. Lets move on assuming the Type is easily accessible.
  • Property “setter”:
     
    • Setters in reflection speak translates into the write method. Property descriptor has getWriteMethod() which returns a Method object that represents the method of the target class. We will use this to generate the build logic

The base template:

I've chosen put all these templates in one template group file (BuilderGenerator.stg). We start with the main class template.

main(class, properties, packageName) ::= <<

package $packageName$;
import $class.name$;

public class $class.simpleName$Builder {


}

>>

 

There are three parameters. The target class, a list of properties and the package name.

contentTemplate.setAttribute("class", Planet.class);
contentTemplate.setAttribute("properties", wrapProperties(Planet.class));
contentTemplate.setAttribute("packageName",“java.net.string.template.demo”);

These three parameters correspond to what gets pushed into the template. We can generate all our code from just these three parameters. (OK, that not totally true. I had to do some wrapping of PropertyDescriptor to make things easier. See the source code at the end of this article for details.)

If we ran the template now against Planet.class we would have an empty class named PlanetBuilder.

  

Builder member variables:

Next we add the template for the private variables.

builderVar(property) ::= << 
private $property.type$ $property.name$;
>>

We need to store our builder arguments somewhere so our build() method can use them later. For each variable we need a type and a name. Since each property has a unique name it's safe to use the property name as private variable name. Now we take the builderVar() template and apply it to our list of properties in the main template.

 

public class $class.simpleName$Builder {

$properties:builderVar()$

...

Running the template now will generate a builder class will all of it's private members declared.

"IsSet" variables:

In Effective Java a builder has defaults specified for all of it's internal variables. Since we are are generating a builder forany class we can't possibly know the proper default value for any particular property. To get around this we need to keep track of which builder methods were called. Here I have split up the template for the name of the variable from it's actual declaration. It will allow me to use isSetVarName later from the build method.

/**
* Tracks if the $property.name$ property has been set on this builder
*/
private boolean $isSetVarName(property)$ = false;$\n$

>>

/**
* Generates the boolean "isSet" variable names
*/

isSetVarName(property) ::= <<$property.name$IsSet>>

Once again we must remember to add this template to the main template.

 

public class $class.simpleName$Builder {

   $properties:builderVar()$
  $properties:isSetVarDec()$

Builder Methods:

This is the public facing API for the builder. We need a name, a parameter type, a parameter name and a return type. The return type is always the builder class. This is what makes fluid interfaces possible. I've used the property name as the builder method name. It takes one parameter of the same type as the property. The “is Set” boolean is set to true when this method is called indicating that this value should be used when constructing a new target object.

public $class.simpleName$Builder $property.name$($property.type$ $property.name$) {
   this.$property.name$ = $property.name$;
   $isSetVarName(property)$ = true;
   return this;
}

Here is what the mass method looks like from Planet.

 

public PlanetBuilder mass(float mass) {
    this.mass = mass;
    massIsSet = true;
    return this;
}

 

Just like the other template this one also needs to be added to the main template.

 

Object Construction:

public $class.simpleName$ build() {
   $class.simpleName$ bean = new $class.simpleName$();
   $properties:apply()$ 
   return bean;
}

This is the final method of our template. Most of this should be familiar by now. The big change is that instead of generating lots of methods or variable declarations we generate blocks of code using the apply template.

apply(property) ::= <<
   if($isSetVarName(property)$){
   bean.$property.writeMethod.name$($property.name$);
}
>>

In this template we start off re-using the isSetVar template to generate the name of the boolean that controls this property. Then we walk the object graph from the property → getWriteMethod → getName() for that actual method name. Since this is a bean property we can assume the parameter count is one and just pass in the builder variable.

Here is what the apply block looks like from the planet class.

if(massIsSet){
    bean.setMass(mass);
}

 

Generating builder classes:

This completes our template. Running the final product on Planet.class now will generate this builder class. We can also point our generator at other beans to generate builders. Here are some examples I've taken from Swing (Swing components are all beans).

JFrameBuilder
JTableBuilder
JComboBoxBuilder
GridLayoutBuilder

Conclution:

I think StringTemplate really made this code generation significantly less complicated. The code generation core is one class, one template file and one extra model object to smooth out the refection api interaction.   Integrating something like this into a build process could help generate more user friendly API's.

 

Now that we have basic functionality, we can start to add more fancy constructs. That though, must wait for another day.

 StringTemplatePart4BuilderGenerator.zip

In Part 1 and Part 2 we dealt with simple data types who's string representation is well known. Complex data types may not have a string representation that meets the needs of a particular template.

// Example 1: Some objects don't have a useful toString()

Object objectParamater = new Object();

StringTemplate template = new StringTemplate("Hello $param$ !");

template.setAttribute("param", objectParamater);

System.out.println(template);

Output:

Hello java.lang.Object@18eb9e6 !

Your output will very but I think it's safe to say that in most cases this output is undesirable. Here is another example of a well intentioned toString() that just won't do for our purposes.

//Example 2: Even a well intentioned toString() methods may not be useful.

Planet mars = createMars();
template = new StringTemplate("Hello $param$ !");
template.setAttribute("param", mars);

System.out.println("Example 2:");
System.out.println(template);

Output:

Example 2:

Hello Planet [atmosphere=CO2 N2, diameter=0.532, inclinationToSunsEquator=5.65, mass=0.11, moons=[Phobos, Deimos], name=Mars, orbitalEccentricity=0.093, orbitalPeriod=1.88, orbitalRadius=1.52, rings=false, rotationPeriod=1.03] !

For object that follow the JavaBean specification (getters and setters) StringTemplate provides property access via the “.” notation. This allows you to access properties from within your template.

//Example 3: Dot notation for property access

template = new StringTemplate("Hello $planet.name$ !");
template.setAttribute("planet", createMars());

System.out.println("Example 3:");
System.out.println(template);

Output:

Example 3:

Hello Mars !

Now we are getting somewhere. The statement planet.name was translated by StringTemplate into a call to getName(). The object we pushed into the template has the name “Mars” so our output became “Hello Mars !”. If we take what we learned about collections (multi-valued attributes) in Part 2 and apply it to a collection of beans we can build more powerful templates.

/**
 * Template to convert a collection of planets into a CSV file.
 */
toCSV(planets) ::= <<
Example 4:
Planet Listing
Name,Mass,Diameter,Orbital Period,Orbital Radius,Orbital Eccentricity,Rotation Period,Inclination To Suns Equator,Rings,Atmosphere,

$planets:planetToCSVRow()$
>>

/**
 * Creates a single row of comma separated values
 */
planetToCSVRow(planet) ::= <<

$planet.name$,$planet.mass$,$planet.diameter$,$planet.orbitalPeriod$,$planet.orbitalRadius$,$planet.orbitalEccentricity$,$planet.rotationPeriod$,$planet.inclinationToSunsEquator$,$planet.rings$,$planet.atmosphere$

>>

This generates CSV data with a header and named columns. I think it's important to note that we pushed data into this template that same way we always have .

template.setAttribute("planets",createPlanets());

We have shifted complexity from the Java code to the template where we can deal with it more effectively. Here is the output from example 4.

Example 4:
Planet Listing

Name,Mass,Diameter,Orbital Period,Orbital Radius,Orbital Eccentricity,Rotation Period,Inclination To Suns Equator,Rings,Atmosphere,
Mercury,0.06,0.382,0.24,0.39,0.206,58.64,3.38,false,Minimal
Venus,0.82,0.949,0.62,0.72,0.0070,-243.02,3.86,false,CO2 N2
Jupiter,317.8,11.209,11.86,5.2,0.048,0.41,6.09,true,H2 HE
Saturn,95.2,9.449,29.46,9.54,0.054,0.43,5.51,true,H2 HE
Neptune,17.2,3.883,164.8,30.06,0.0090,0.67,6.43,true,H2 HE
Earth,1.0,1.0,1.0,1.0,0.017,1.0,7.25,false,N2 O2
Mars,0.11,0.532,1.88,1.52,0.093,1.03,5.65,false,CO2 N2

Here we have nice CSV output. When a column is added or removed all we need to do it make the appropriate change in the template. The source code does not need to change. Static header or footer information can be added with no impact on Java code.

All planetary data was taken from: http://en.wikipedia.org/wiki/Planet

Renderers:

In all our previous examples we could, at some point, obtain an adequate string representation of our data without fiddling around too much. This will not always be the case. As an example of how to deal with this I will use a data type notorious for needing special attention, java.util.Date. Lets start simply with the following Java code.

// Example 5: Unformatted Date
template = new StringTemplate("The current date is: $now$ !");
template.setAttribute("now", new Date());

System.out.println("Example 5:");
System.out.println(template); 

Output:

Example 5:
The current time is: Fri Jun 04 08:01:48 CDT 2010 !

 

While this does accurately report the current time it may not be in a form familiar to your users. In this case there is no property that we can use as our preferred display value. StringTemplate provides a system for intercepting an object before it gets turned into a string. This is called a “Renderer”.

Here is the skeleton of a simple Renderer. ( AttributeRenderer has a second method but lets ignore that for now.)

public class DateRenderer implements AttributeRenderer {

    @Override
    public String toString(Object attribute) {
        return null;
    }
}

The original attribute is passed as a parameter to the toString() method. The return value from the toString() method becomes the new value placed in the template. For Dates we can leverage the SimpleDateFormat (http://java.sun.com/javase/6/docs/api/java/text/SimpleDateFormat.html) class to do the actual formatting.

public class StaticDateRenderer implements AttributeRenderer{

    private SimpleDateFormat format;

    public StaticDateRenderer(String pattern){
        format = new SimpleDateFormat(pattern);
    }

    @Override
    public String toString(Object attribute) {
        return format.format((Date) attribute);
   }
   ...
}

As you can see we are just delegating to an instance of SimpleDateFormat. The cast to a Date is always safe because we register a rendered by the class literal.

template.registerRenderer(Date.class,new StaticDateRenderer("h:m:ss a"));

When we add the above line to our previous example we see that the date now appears in a more familiar from.

Example 5: Using a Date Renderer

The current time is: 7:51:08 AM !

Renders are specified per class. This means that if we had two dates in the above example they would both use the “h:m:ss a” format. This would be undesirable if for instance we had two column in a table, one was a date and one was a time. Luckily AttributeRenderer provides a second method to help deal with this.

public String toString(Object attribute, String format)

This method takes the attribute to be rendered and a format parameter. This format parameter is passed from the template to the render.

$now; format="SHORT"$

In this case the toString method is passed the value 'SHORT'. It's up to the render to decide what to do with any particular parameter. DynamicDateRenderer is an example of a render that utilizes the format parameter to effect the formatting of a particular date. It supports the formatting styles from DateFormat and the SimpleDateFormat pattern syntax.

Example 6: Formatting Dates

DateFromat Styles:

    DEFAULT     $now$
    SHORT       $now; format="SHORT"$
    MEDIUM      $now; format="MEDIUM"$
    LONG        $now; format="LONG"$
    FULL        $now; format="FULL"$

Using SimpleDateFormat Patterns:

    yyyy.MM.dd G 'at' HH:mm:ss z        $now; format="yyyy.MM.dd G 'at' HH:mm:ss z"$
    EEE, MMM d, ''yy                    $now; format="EEE, MMM d, ''yy"$ 
    h:mm a                              $now; format="h:mm a"$
    hh 'o''clock' a, zzzz               $now; format="hh 'o''clock' a, zzzz"$
    K:mm a, z                           $now; format="K:mm a, z"$
    yyyyy.MMMMM.dd GGG hh:mm aaa        $now; format="yyyyy.MMMMM.dd GGG hh:mm aaa"$
    EEE, d MMM yyyy HH:mm:ss Z          $now; format="EEE, d MMM yyyy HH:mm:ss Z"$
    yyMMddHHmmssZ                       $now; format="yyMMddHHmmssZ"$
    yyyy-MM-dd'T'HH:mm:ss.SSSZ          $now; format="yyyy-MM-dd'T'HH:mm:ss.SSSZ"$
    dd.MM.yy                            $now; format="dd.MM.yy"$
    h:mm a                              $now; format="h:mm a"$
    H:mm                                $now; format="H:mm"$
    H:mm:ss:SSS                         $now; format="H:mm:ss:SSS"$  

The template uses the same variable in every line, only the formatting parameter changes. On one side I have put the description of the date format. On the other side I have applied the format to the date value. Below is the final output from the above template.

Example 6: Formatting Dates

DateFromat Styles:

    DEFAULT     Jun 7, 2010
    SHORT       6/7/10
    MEDIUM      Jun 7, 2010
    LONG        June 7, 2010
    FULL        Monday, June 7, 2010

Using SimpleDateFormat Patterns:

    yyyy.MM.dd G 'at' HH:mm:ss z        2010.06.07 AD at 21:00:18 CDT
    EEE, MMM d, ''yy                    Mon, Jun 7, '10 
    h:mm a                              9:00 PM
    hh 'o''clock' a, zzzz               09 o'clock PM, Central Daylight Time
    K:mm a, z                           9:00 PM, CDT
    yyyyy.MMMMM.dd GGG hh:mm aaa        02010.June.07 AD 09:00 PM
    EEE, d MMM yyyy HH:mm:ss Z          Mon, 7 Jun 2010 21:00:18 -0500
    yyMMddHHmmssZ                       100607210018-0500
    yyyy-MM-dd'T'HH:mm:ss.SSSZ          2010-06-07T21:00:18.668-0500
    dd.MM.yy                            07.06.10
    h:mm a                              9:00 PM
    H:mm                                21:00
    H:mm:ss:SSS                         21:00:18:668

Renders are a powerful system for converting data into template friendly forms. As a rule of thumb when you are combining data you should use a template. When the data itself needs to take on a different form it's time to consider a renderer.

This concludes part 3. I hope you give StringTemplate a try. As always, if you want more information please see the official StringTemplate documentation.

Example Code for Part 3

This Article deals with StringTemplate. If you've never heard of StringTemplate or a “template engine” you might want to read either Part 1 or the official StringTemplate documentation.

Template Group Files:

In the first article we used a template file (.st file) to hold our template definition. In those examples we had defined a single template that spanned the entire file. When things get more complicated it's convenient to be able to define multiple smaller templates in one file. StringTemplate calls this file a “String Template Group” and recommends a filename ending in “.stg”.

Example String Template Group:

This template group defines one template that takes two parameters, input1 and input2. Parameters passed to the template can then be used in the content area.

Example template with content:

exampleTemplate(input1, input2) ::= <<
    input1 = $input1$
    input2 = $input2$
>>

StringTemplateGroups are a convenient way to group small interrelated templates. These smaller templates can be nested as shown below.

group group-demo;

outerTemplate(input) ::= <<
    In the outer template.
    input = $input$ <-- I can see the value of the 'input' parameter and use it.
    $innerTemplate(input)$
>>

innerTemplate(nestedInput) ::= <<
    The Paramater 'nestedInput' was passed to this template.
    nestedInput = $nestedInput$ <-- I can see the value of the 'nestedInput' parameter and use it.
>>

Here is the code to use this StringTemplateGroup:

StringTemplateGroup group = new StringTemplateGroup(
    new FileReader("templates/group-example.stg"), DefaultTemplateLexer.class);

StringTemplate template = group.getInstanceOf("outerTemplate");
template.setAttribute("input", "Hello World");

System.out.println(template.toString()); 

The output form this example will look much like this:

In the outer template.
   
input = Hello World; <-- I can see the value of the 'input' parameter and use it.
   
The Paramater 'nestedInput' was passed to this template. I can use it here also.

nestedInput = Hello World <-- I can see the value of the 'nestedInput' parameter and use it.

Collections (Multi-Valued Attributes):

In the first article I showed some basic template tasks. Each example mapped a placeholder to a single piece of data. Very often complicated structures require dealing with collections of data that do not match one to one to placeholders in a template. In Java if we had to process the items in a list we might use a loop.

List<String> xmen = Arrays.asList("Jean Gray", "Cyclops",
    "Angel", "Iceman", "Beast");

System.out.println("Original X-Men");
for(String xman : xmen){
    System.out.println(xman);
}

Output:

Original X-Men
Jean Gray
Cyclops
Angel
Iceman
Beast

A valiant first attempt at using StringTemplate might result in code that looks like this:

StringTemplate template = new StringTemplate(
    "Example 3\nOriginal X-MEN: $xmen$ ");
   
template.setAttribute("xmen", xmen);

System.out.println(template.toString());

As you can see we took the list of strings and just pushed it into the template. Unfortunately this won't give us the same output as above.

Output:

Example 3

Original X-MEN: Jean GrayCyclopsAngelIcemanBeast

Whats happening here is that we gave a “multi-valued attribute” (collection) to StringTemplate but did not tell it how to handle that data. One way to accomplish this is to use a separator.

Example 4:

Original X-MEN members:
$xmen; separator="\n"$

Output:

Example 4:

Original X-MEN members:
Jean Gray
Cyclops
Angel
Iceman
Beast

As you can see we specified a separator of “\n” which means newline. There is no restriction on the size of a separator. The following:

$xmen; separator=" \"\$hi, this is a really long separator\$\"\n\n"$

would yield the following output.

Original X-MEN members:

Jean Gray "$hi, this is a really long separator$"

Cyclops "$hi, this is a really long separator$"

Angel "$hi, this is a really long separator$"

Iceman "$hi, this is a really long separator$"

Beast

I have embedded both quotes and dollar signs to emphasize that you really can use anything in a separator. (If you really want to but I don't recommend it.)

Applying Templates to multi-valued attributes:

Separators have their place but I find that applying a template to a multi-valued attribute significantly more flexible and powerful. I also feel it's one of the key differentiators between StringTemplate and other template engines. First lets look at some good (bad?) old fashioned Java string building.

// Manual formatting and spacing
StringBuilder builder = new StringBuilder();
builder.append("Example 2 \n");
builder.append("<html><body>\n");
builder.append("<h1>Original X-Men</h1>\n");
builder.append(" <ul>\n");
for(String xman : xmen){
    builder.append(" <li>"+ xman + "</li>\n");
}
builder.append(" </ul>\n");
builder.append("</body></html>\n");

Here is some standard java code for building up a block of HTML. Note there are two different levels of formatting here. One is the HTML, which has requirements like the opening and closing of tags. The other is the actual formatting of the output text itself. This is done to keep the generated block readable after it's generated. Whenever text is appended both formatting concerns need to be taken into account. Unlike many of the examples from Part 1 this has some flow control going on. Specifically, we have a loop. When we translate this logic to a template, or more specifically two templates, we can eliminate the details of the loop.

group list-demo;

htmListExample(xmen) ::= <<
Example 5:
<html>
    <body>
        <h1>Original X-Men</h1>
        <ul>
            $xmen:listItem()$
        </ul>
    </body>
</html>

>>

listItem() ::= <<

<li>$it$</li>
>>

Here we have the contents of our template file “list-template-group.stg”. We have defined two templates “htmlListExample” and “listItem”. From our previous examples we know that xmen is a multi-valued attribute. The : syntax is used to apply the listItem template to every item in the list. This generates the following output:

Output:

Example 5:
<html>
    <body>
      <h1>Original X-Men</h1>
      <ul>
   
        <li>Jean Gray</li>
        <li>Cyclops</li>
        <li>Angel</li>
        <li>Iceman</li>
        <li>Beast</li>
      </ul>
    </body>
</html>

As you can see there is no loop, no loop counter and we didn't even define a variable in our second template. StringTemplate makes the $it$ variable available if you have not explicitly named one. Now to be honest this output is not the exact same output as the above block of Java code. There is an extra new line at the beginning of the list items. Most of the time I find this to be “good enough” for the “readability” formatting, but there is a way to fix it. StringTemplate provides functions for slicing a list up into manageable parts. Here we are going to use first() and rest().

/**
 * HTML example for applying a template to a list using first() and rest()
 */
firstRestExample(xmen) ::= <<

Example 6:
<html>
  <body>
    <h1>Original X-Men</h1>
    <ul>
        $first(xmen):firstListItem()$$rest(xmen):listItem()$
    </ul>
  </body>
</html>
>>

firstListItem() ::= "<li>$it$</li>"

The first() function returns only the first element of the multi-valued attribute. We then apply the firstListItem() template to only the first item. Everything except the first element (returned by rest()) has the listItem() template applied to it. This generates the exact same output as the block of old fashioned Java code.

Example 6:
<html>
  <body>
    <h1>Original X-Men</h1>
    <ul>
        <li>Jean Gray</li>
        <li>Cyclops</li>
        <li>Angel</li>
        <li>Iceman</li>
        <li>Beast</li>
    </ul>
  </body>
</html>

In this example the use of first and rest was used to solve a trivial whitespace issue. In more complicated templates the functions first(), rest(), etc. may solve more important problems. Finally you are not restricted to applying just one template to an item in a list.

/**
 * HTML example for applying multiple templates to a list.
 */
applyMultipleExample(xmen) ::= <<

Example 7:
<html>
  <body>
    <h1>Original X-Men</h1>
    <ul>
        $xmen:bold():listItem()$
    </ul>
  </body>
</html>
>>

/**
 * wraps the text in a <span> with a style of bold
 */
bold() ::= <<
<span style="font-weight: bold;">$it$</span>
>>

For each value of the multi-valued attribute the templates are applied from left to right. In this case bold is applied first then list item.

Output:

Example 7:
<html>
  <body>
    <h1>Original X-Men</h1>
    <ul>

        <li><span style="font-weight: bold;">Jean Gray</span></li>
        <li><span style="font-weight: bold;">Cyclops</span></li>
        <li><span style="font-weight: bold;">Angel</span></li>
        <li><span style="font-weight: bold;">Iceman</span></li>
        <li><span style="font-weight: bold;">Beast</span></li>
    </ul>
  </body>

</html>

In this article we have explored string template group files, defining multiple templates withing a single file and applying templates to multi-values attributes (collections). If you want to learn more I recommend theofficial StringTemplate documentation. It has more detailed explanations and examples.

 Source Code: StringTemplateDemos2.zip

Please note: I put forth SQL examples because they can be short and are easy to understand. As other have pointed out it is not advisable to build SQL with Strings, regardless of the technique used.

What is StringTemplate?

To quote the StringTemplate home page:
         “StringTemplate is a java template engine for generating source code, web pages, emails, or any other formatted text output.”

Great, but what is a “template engine”?
          A template engine (also known as a template processor or a template parser) is a software component that is designed to combine one or more templates with a data model to produce one or more result documents (Wikipedia). To you and me that means that a template engine is an alternate way to generate complicated text. One that I feel is particularity useful. Before we dive into that, lets take a look at a few examples of traditional String construction in Java.

First we have the trusty old “+” operator.

String message = "Hello" + " World";

While convenient for small concatenations. It becomes a real pain when the embedded variable count of the amount of text increases.      

String strSQL = "UPDATE customers SET customerName='" +name
     +
"', customerAddress='" + address
     +
"', customerEmail = '”+ email + "
     +
"customerNumber = " +phone
     +" customerPurchase = " + purchase
     +
"' WHERE id=" + 12;

 

String new419 = "Hello "
     +
"Dear, " + name+ ", \n"
     +"Permit me to inform you of my desire of "
     +
"going into business relationship with you. "
     + "I have the believe you are a reputable "
     + "and responsible and trustworthy person \n"
     + "I can do business with from the little"
     +"information so far I gathered about you "
     +"during my search for a partner and by matter "
     +"of trust I must not hesitate to confide "
     +
"in you for this simple and sincere business. ";

Next we have StringBuilder. In general it's considered a better practice to use StringBuilder because of it's performance profile.   

StringBuilder sqlBuilder = new StringBuilder(
     "UPDATE customers SET customerName='").append(name).append(
     "', customerAddress='").append(address).append(
     "', customerEmail = '").append(email).append(
     "', customerNumber = '").append(phone).append(
     "', customerPurchase = '"
).append(purchase).append(
     "' WHERE id=").append(12);

Unfortunately it doesn't make building up a string significantly easier.
 

Finally we have formatted text

// Explicit argument indices may be used to re-order output.
String.format("%4$2s %3$2s %2$2s %1$2s", "a", "b", "c", "d");
>> d c b a

// Optional locale as the first argument can be used to get
// locale-specific formatting of numbers. The precision and width can be given
// to round and align the value.

String.format(Locale.FRANCE, "e = %+10.4f", Math.E);

>> e = +2,7183

// The '(' numeric flag may be used to format negative numbers with
// parentheses rather than a minus sign. Group separators are
// automatically inserted.

String.format("Amount gained or lost since last statement: $ %(,.2f",balanceDelta);

>> Amount gained or lost since last statement: $ (6,217.58)

Format is powerful but only scales up to, at most, a handful of parameters. This is not because there is some programmatic limit to the Format class, but more one of practicality. Tracking a hundred parameters by their index would be quite challenging.

Templates, a better way.

A template is simply a document with “placeholders” in it. These “placeholders” are expressions that tell the template engine where to put data.  All (okay most) white space and line feed characters are respected. This also has the nice benefit of separating the formatting (view) from the data. Here is a simple single line example (with StringTemplate syntax inred):

StringTemplate query = new StringTemplate(
     "UPDATE customers SET customerName='$customer$'," +
     " customerAddress='$address$' WHERE id=$id$"
);

query.setAttribute("customer", "Frank");
query.setAttribute(
"address", "1313 Mocking Bird Lane");
query.setAttribute(
"id", "4453");

System.out.println(query);

Output:

UPDATE customers SET customerName='Frank', customerAddress='1313 Mocking Bird Lane' WHERE id=4453

As you can see “placeholders” are delimited by $...$ (or <...> if you so choose). In this way it is reminiscent of variable substitution in languages like PHP or bash. The name inside the delimiters is used to look up the correct piece of data. Having names is important for maintaining readability. It also allows data to be pushed into the template in any order. This was a single line example. When you get to multi-line text you will probably want to use a template file. A template file is a plain text document that contains the fully formatted version of your desired output. $...$ attributes are placed thought the document to indicate parts you want replaced later by StringTemplate.

Example template file (spam-419.st):

From Address $from_addr$
To Address $to_addr$
Dear $to$,
    Permit me to inform you of my desire of going into business relationship with you. I have
    the believe you are a reputable and responsible and trustworthy person I can do business
    with from the little information so far I gathered about you during my search for a partner
    and by matter of trust I must not hesitate to confide in you for this simple and sincere business
.
...
Best Regards, $from$

Populating this template involves loading a StringTemplateGroup and using that to create an instance of your StringTemplate.  

StringTemplateGroup templateGroup = new StringTemplateGroup("spam group", "templates");
StringTemplate spam419 = templateGroup.getInstanceOf(
"spam419");
spam419.setAttribute(
"to", "Collin");
spam419.setAttribute(
"to_addr", "collin@bugmenot.com");
spam419.setAttribute(
"from", "Ima Spammer");
spam419.setAttribute(
"from_addr", "ima.spammer@spammers-paradise.com");

 

System.out.println(spam419.toString());

The StringTemplateGroupconstructor in the code above takes two parameters. The first is a "group name". In this example it is not really important, feel free to use anything you like. The second is very important. This is the location of a directory (folder) that contains the template files you wish to load. In this example I am using a relative path. Relative to where I'm executing this code I expect to see a directory called "templates". ThegetInstanceOf() method takes the name (without the .st extension) of the that template file to be loaded. In this case I'm attempting to load "spam419.st".

From Address ima.spammer@spammers-paradise.com
To Address collin@bugmenot.com
Dear Collin,
    Permit me to inform you of my desire of going into business relationship with you. I have
    the believe you are a reputable and responsible and trustworthy person I can do business
    with from the little information so far I gathered about you during my search for a partner
    and by matter of trust I must not hesitate to confide in you for this simple and sincere business
.
...
Best Regards, Ima Spammer

I encourage you to experiment with this template. I think you will find adding or removing lines is trivial. Changing white space in the template is trivial. Even adding new attributes (placeholders) is straight forward. StringTemplate is a powerful tool that I can't hope to cover in just one blog post. For those who are chomping at the bit to learn more I'd recommend reading theofficial StringTemplate documentation which is a fantastic resource. 

Have fun,
Collin

Example sources

Topic: The Seductions of Scala Scala is a new, statically-typed language for the JVM. Its advantages over Java include a succinct syntax, an improved object model and full support for functional programming (FP). In this code-heavy talk, I

Lately I've been spending my spare time contributing code samples to the The Java Tutorial Community Portal. My hope is that one or more of these examples will be interesting enough to be included in the official tutorial. My latest addition is an example on how to override tool tip generation for a particular component. This example is a tabbed pane that shows a preview of a tab as a tooltip.

https://javatutorials.dev.java.net/FORUM/images/ThumbnailTooltipDemo.png

In this picture the mouse was hovering over the "images" tab.

There are techniques for installing custom universal tool tip providers. In this case I only wanted to change the behaviour for one particular component. To accomplish this I overrode the createToolTip() method of JTabbedPane with the following:

/**
 * overide createToolTip() to return a JThumbnailToolTip
 */
@Override
public JToolTip createToolTip() {
    Point loc = getMousePosition();
    int index = indexAtLocation(loc.x, loc.y);

    if (index != -1) {
        Component comp = getComponentAt(index);
        
        BufferedImage image = ImageFactory.toImage(comp);
        
        JToolTip tip = new JImageToolTip(image, 0.5);
        
        return tip;
    } else {
        return super.createToolTip();
    }
}

This method returns an instance of JImageToolTip which displays an image of the hidden component instead of the tooltip text. The image of the component was generated by the toImage() method of my ImageFactory class.

/**
 * Generates a buffered Image of the specified component.
 * 
 * @param componet
 * @return image 'snapshot' of the component and it's state at the time this
 *         method was called.
 */
public static BufferedImage toImage(Component componet) {
    BufferedImage image = new BufferedImage(componet.getWidth(), componet
            .getHeight(), BufferedImage.TYPE_INT_RGB);
    Graphics graphics = image.getGraphics();
    graphics.setColor(componet.getBackground());
    graphics.fillRect(0, 0, componet.getWidth(), componet.getHeight());
    componet.paint(graphics);
    graphics.dispose();

    return image;
}

The JImageToolTipThis also class was used to create a label that displays a thumbnail of an image and provides the full size image as a tooltip. 

https://javatutorials.dev.java.net/FORUM/images/ThumbnailTooltipDemo2.png

I've made the full source code available. Comments on this or any other code sample are welcomed and encouraged.

https://javatutorials.dev.java.net/images/jws-launch-button.png

Source Code: ThumbnailTooltipDemo-src.zip

Filter Blog

By date: