DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • How To Build AI-Powered Prompt Templates Using the Salesforce Prompt Builder
  • Automated Bug Fixing: From Templates to AI Agents
  • Dynamic File Upload Component in Salesforce LWC
  • Safeguarding Web Applications With Cloud Service Providers: Anti-CSRF Tokenization Best Practices

Trending

  • Can You Run a MariaDB Cluster on a $150 Kubernetes Lab? I Gave It a Shot
  • Building a Real-Time Audio Transcription System With OpenAI’s Realtime API
  • AI Speaks for the World... But Whose Humanity Does It Learn From?
  • The Evolution of Scalable and Resilient Container Infrastructure

StringTemplate Part 2: Collections and Template Groups

By 
Collin Fagan user avatar
Collin Fagan
·
Jun. 03, 10 · Interview
Likes (0)
Comment
Save
Tweet
Share
12.8K Views

Join the DZone community and get the full member experience.

Join For Free

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 the official StringTemplate documentation. It has more detailed explanations and examples.

 Source Code: StringTemplateDemos2.zip

From http://weblogs.java.net/blog/aberrant/archive/2010/06/02/stringtemplate-part-2-collections-and-template-groups

Template

Opinions expressed by DZone contributors are their own.

Related

  • How To Build AI-Powered Prompt Templates Using the Salesforce Prompt Builder
  • Automated Bug Fixing: From Templates to AI Agents
  • Dynamic File Upload Component in Salesforce LWC
  • Safeguarding Web Applications With Cloud Service Providers: Anti-CSRF Tokenization Best Practices

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!