https://www.x5software.com/chunk/wiki/index.php?title=Special:Contributions/Tom_McClure&feed=atom&limit=50&target=Tom_McClure&year=&month=Chunk Java Template Engine - User contributions [en]2024-03-28T11:53:37ZFrom Chunk Java Template EngineMediaWiki 1.16.0https://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2022-01-03T19:39:10Z<p>Tom McClure: /* Introduction */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.6.2</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.6.2</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
{{blockquote|<br />
'''Missing Templates'''<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
If you don't want these errors to pass silently, you can call <code>theme.setAbortOnMissingTemplate(true)</code> and the engine will instead throw a TemplateNotFoundException.<br />
}}<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
Make sure your locale folder contains one folder for each locale code, with a csv file named translate.csv in each one.<br />
<br />
locale/<br />
|-- de_DE/<br />
| +-- translate.csv<br />
|-- es_ES/<br />
| +-- translate.csv<br />
+-- fr_FR/<br />
+-- translate.csv<br />
<br />
Expected encoding for csv files is UTF-8. Set the system property chunk.localedb.charset to override.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the "translate" filter, like so:<br />
<br />
{$tag|translate}<br />
<br />
or use these shortcut aliases "xlate" or "_" like so:<br />
<br />
{$tag|xlate} {$tag|_}<br />
<br />
=== Custom Locale files path ===<br />
<br />
If you want to place translation csv files on the filesystem and not as resources in the classpath, set the System property chunk.localedb.path to the location of the "locale" folder.<br />
<br />
=== Custom TranslationsProvider ===<br />
<br />
Some deployment environments (for example, Android) have neither access to the filesystem nor access to resources from the classpath.<br />
<br />
You can write your own class which implements com.x5.template.providers.TranslationsProvider<br />
and pass it to the Theme object:<br />
<br />
class MyTranslationsProvider implements TranslationsProvider {<br />
public Map<String, String> getTranslations(String localeCode) {<br />
// ... load translations from app context assets? ...<br />
}<br />
}<br />
<br />
// ...<br />
Theme theme = new Theme();<br />
theme.setTranslationsProvider(new MyTranslationsProvider());<br />
theme.setLocale("fr_FR");<br />
Chunk chunk = theme.makeChunk()<br />
// ...<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2022-01-03T19:38:09Z<p>Tom McClure: /* How to get Chunk */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.6.2</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
{{blockquote|<br />
'''Missing Templates'''<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
If you don't want these errors to pass silently, you can call <code>theme.setAbortOnMissingTemplate(true)</code> and the engine will instead throw a TemplateNotFoundException.<br />
}}<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
Make sure your locale folder contains one folder for each locale code, with a csv file named translate.csv in each one.<br />
<br />
locale/<br />
|-- de_DE/<br />
| +-- translate.csv<br />
|-- es_ES/<br />
| +-- translate.csv<br />
+-- fr_FR/<br />
+-- translate.csv<br />
<br />
Expected encoding for csv files is UTF-8. Set the system property chunk.localedb.charset to override.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the "translate" filter, like so:<br />
<br />
{$tag|translate}<br />
<br />
or use these shortcut aliases "xlate" or "_" like so:<br />
<br />
{$tag|xlate} {$tag|_}<br />
<br />
=== Custom Locale files path ===<br />
<br />
If you want to place translation csv files on the filesystem and not as resources in the classpath, set the System property chunk.localedb.path to the location of the "locale" folder.<br />
<br />
=== Custom TranslationsProvider ===<br />
<br />
Some deployment environments (for example, Android) have neither access to the filesystem nor access to resources from the classpath.<br />
<br />
You can write your own class which implements com.x5.template.providers.TranslationsProvider<br />
and pass it to the Theme object:<br />
<br />
class MyTranslationsProvider implements TranslationsProvider {<br />
public Map<String, String> getTranslations(String localeCode) {<br />
// ... load translations from app context assets? ...<br />
}<br />
}<br />
<br />
// ...<br />
Theme theme = new Theme();<br />
theme.setTranslationsProvider(new MyTranslationsProvider());<br />
theme.setLocale("fr_FR");<br />
Chunk chunk = theme.makeChunk()<br />
// ...<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2021-03-22T17:39:40Z<p>Tom McClure: /* Chunk _[Localization] */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.6.0</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
{{blockquote|<br />
'''Missing Templates'''<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
If you don't want these errors to pass silently, you can call <code>theme.setAbortOnMissingTemplate(true)</code> and the engine will instead throw a TemplateNotFoundException.<br />
}}<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
Make sure your locale folder contains one folder for each locale code, with a csv file named translate.csv in each one.<br />
<br />
locale/<br />
|-- de_DE/<br />
| +-- translate.csv<br />
|-- es_ES/<br />
| +-- translate.csv<br />
+-- fr_FR/<br />
+-- translate.csv<br />
<br />
Expected encoding for csv files is UTF-8. Set the system property chunk.localedb.charset to override.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the "translate" filter, like so:<br />
<br />
{$tag|translate}<br />
<br />
or use these shortcut aliases "xlate" or "_" like so:<br />
<br />
{$tag|xlate} {$tag|_}<br />
<br />
=== Custom Locale files path ===<br />
<br />
If you want to place translation csv files on the filesystem and not as resources in the classpath, set the System property chunk.localedb.path to the location of the "locale" folder.<br />
<br />
=== Custom TranslationsProvider ===<br />
<br />
Some deployment environments (for example, Android) have neither access to the filesystem nor access to resources from the classpath.<br />
<br />
You can write your own class which implements com.x5.template.providers.TranslationsProvider<br />
and pass it to the Theme object:<br />
<br />
class MyTranslationsProvider implements TranslationsProvider {<br />
public Map<String, String> getTranslations(String localeCode) {<br />
// ... load translations from app context assets? ...<br />
}<br />
}<br />
<br />
// ...<br />
Theme theme = new Theme();<br />
theme.setTranslationsProvider(new MyTranslationsProvider());<br />
theme.setLocale("fr_FR");<br />
Chunk chunk = theme.makeChunk()<br />
// ...<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2021-03-22T17:36:12Z<p>Tom McClure: /* Chunk _[Localization] */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.6.0</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
{{blockquote|<br />
'''Missing Templates'''<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
If you don't want these errors to pass silently, you can call <code>theme.setAbortOnMissingTemplate(true)</code> and the engine will instead throw a TemplateNotFoundException.<br />
}}<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
Make sure your locale folder contains multiple folders named for each locale code, with a csv file named translate.csv in each one.<br />
<br />
locale/<br />
|-- de_DE/<br />
| +-- translate.csv<br />
|-- es_ES/<br />
| +-- translate.csv<br />
+-- fr_FR/<br />
+-- translate.csv<br />
<br />
Expected encoding for csv files is UTF-8. Set the system property chunk.localedb.charset to override.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the "translate" filter, like so:<br />
<br />
{$tag|translate}<br />
<br />
or use these shortcut aliases "xlate" or "_" like so:<br />
<br />
{$tag|xlate} {$tag|_}<br />
<br />
=== Custom Locale files path ===<br />
<br />
If you want to place translation csv files on the filesystem and not as resources in the classpath, set the System property chunk.localedb.path to the location of the "locale" folder.<br />
<br />
=== Custom TranslationsProvider ===<br />
<br />
Some deployment environments (for example, Android) have neither access to the filesystem nor access to resources from the classpath.<br />
<br />
You can write your own class which implements com.x5.template.providers.TranslationsProvider<br />
and pass it to the Theme object:<br />
<br />
class MyTranslationsProvider implements TranslationsProvider {<br />
public Map<String, String> getTranslations(String localeCode) {<br />
// ... load translations from app context assets? ...<br />
}<br />
}<br />
<br />
// ...<br />
Theme theme = new Theme();<br />
theme.setTranslationsProvider(new MyTranslationsProvider());<br />
theme.setLocale("fr_FR");<br />
Chunk chunk = theme.makeChunk()<br />
// ...<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2021-03-22T04:23:55Z<p>Tom McClure: /* Chunk _[Localization] */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.6.0</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
{{blockquote|<br />
'''Missing Templates'''<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
If you don't want these errors to pass silently, you can call <code>theme.setAbortOnMissingTemplate(true)</code> and the engine will instead throw a TemplateNotFoundException.<br />
}}<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
Make sure your locale folder contains multiple folders named for each locale code, with a csv file named translate.csv in each one.<br />
<br />
locale/<br />
|-- de_DE/<br />
| +-- translate.csv<br />
|-- es_ES/<br />
| +-- translate.csv<br />
+-- fr_FR/<br />
+-- translate.csv<br />
<br />
Expected encoding for csv files is UTF-8. Set the system property chunk.localedb.charset to override.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the "translate" filter, like so:<br />
<br />
{$tag|translate}<br />
<br />
or use these shortcut aliases "xlate" or "_" like so:<br />
<br />
{$tag|xlate} {$tag|_}<br />
<br />
=== Custom Locale files path ===<br />
<br />
If you want to place translation csv files on the filesystem and not as resources in the classpath, set the System property chunk.localedb.path to the location of the "locale" folder.<br />
<br />
=== Custom TranslationsProvider ===<br />
<br />
Some deployment environments (for example, Android) have neither access to the filesystem nor access to resources from the classpath.<br />
<br />
You can write your own class which implements com.x5.template.providers.TranslationsProvider<br />
and pass it to the Theme object:<br />
<br />
class MyTranslationsProvider implements TranslationsProvider {<br />
public Map<String, String> getTranslations(String localeCode) {<br />
// ... load translations from app context assets? ...<br />
}<br />
}<br />
<br />
// ...<br />
Theme theme = new Theme();<br />
theme.setTranslationsProvider(new MyTranslationsProvider());<br />
theme.setLocale("fr_FR");<br />
Chunk chunk = theme.makeChunk()<br />
// ...<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_Tag_FiltersChunk Tag Filters2021-03-22T04:18:00Z<p>Tom McClure: /* join[()] */</p>
<hr />
<div>Chunk includes several useful filters right out of the box, eg <code>{$tag|upper}</code>.<br />
<br />
Filters may be chained ad infinitum: <code>{$tag|md5hex|upper}</code>.<br />
<br />
== alternate(<evenoutput>,<oddoutput>) ==<br />
When tag value is even, output <evenoutput> - when odd, output <oddoutput>. Unbraced tags are valid arguments, eg:<br />
<br />
output the strings "even" and "odd":<br />
{% $i|alternate(even,odd) %}<br />
<br />
output {$even} and {$odd} tags:<br />
{% $i|alternate($even,$odd) %}<br />
<br />
include templates {#even} and {#odd}<br />
{% $i|alternate(+#even,+#odd) %}<br />
<br />
Alias: evenodd<br />
<br />
== base64 ==<br />
Base64-encode.<br />
<br />
== base64decode ==<br />
Base64-decode.<br />
<br />
== bool ==<br />
Translates "truthy" values to "TRUE"/null for use in "if" expressions.<br />
<br />
eg: {% if ($n|calc($x * $x > 20)|bool) %} ... {% endif %}<br />
<br />
<table><br />
<tr><br />
<th></th><br />
<th align="left">Input</th><br />
<th align="left">Output</th><br />
</tr><br />
<tr><br />
<td></td><br />
<td>null</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td align="right">empty string:&nbsp;</td><br />
<td>""</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td align="right">whitespace-only string:&nbsp;</td><br />
<td>" "</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td align="right">any defined, non-empty string:&nbsp;</td><br />
<td>"bananas"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"0"</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"1"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"2"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"-1"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"0.0"</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"1.0"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"21.0"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"-1.0"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"true"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"false"</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"True"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"False"</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"TRUE"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"FALSE"</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>non-null object</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>null object</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td align="right">the string "null":&nbsp;</td><br />
<td>"null"</td><br />
<td>TRUE</td><br />
</tr><br />
</table><br />
<br />
Alias: boolean<br />
<br />
== calc(<expr>[,<fmt>]) ==<br />
Requires jeplite - see [https://github.com/tomj74/chunk-templates GitHub project page] for download instructions.<br />
<br />
The calc filter is similar to the comp filter but provides much richer expression parsing and simple $tag references.<br />
<br />
If $x appears in <expr> then the input value is substituted for $x, otherwise the input value is prepended to the expression.<br />
<br />
The input may be filtered before passing to the calc filter, but direct $tag references in the expression may not use any filter/default modifiers.<br />
<br />
Examples:<br />
<br />
{% $n:2|calc(+2) %}<br/><br />
output: 4.0<br />
<br />
{% $n:2|calc("$x+$x") %}<br/><br />
output: 4.0<br />
<br />
{% $n:0.5|calc("sin(pi*$x)","%.2f") %}<br/><br />
output: 1.00<br />
<br />
{% $a|calc("(-$b + sqrt($b^2 - 4*$x*$c))/(2*$x)") %}<br/><br />
output: well, it depends on values of $a $b $c ...<br />
<br />
{{blockquote|<br />
'''Notes'''<br />
* Non-numeric input (including null/undefined) will pass through the filter with no processing and the expression will not be executed. <br />
* Behavior when referencing non-numeric tag values in expressions is undefined<br />
* Functions available: sin cos tan asin acos atan sqrt log ln angle abs mod sum rand umin add<br />
* Computational operators: * / + - ^ %<br />
* Comparison operators: > < >&#61; <&#61; &#61;&#61; !&#61; <><br />
* Grouping operators: ( )<br />
* Constants available: e and pi<br />
}}<br />
<br />
Jeplite evaluates comparisons as 0.0 for false and 1.0 for true, so that output must be passed through the "bool" filter if you wish to use it in an "if" expression.<br />
<br />
== capitalize ==<br />
Uppercase the first letter of each word. Leave case of other characters intact. See [[#title|title]].<br />
<br />
Alias: cap<br />
<br />
== checked(<testvalue>[,<output>]) ==<br />
<br />
Output checked="checked" (or <output> if specified) when tag value matches <testvalue>. Argument may be an unbraced $tag.<br />
<br />
== comp(<operation>) ==<br />
Perform a limited set of comparisons (> < >= <= <> == !=) or mathematical transformations (+, -, *, /, %, ^) on an integer (long) or floating-point (decimal) input. eg: <code>{% $tag|qcalc(+20) %}</code>.<br />
<br />
The <operation> argument syntax is very strict: an operator symbol must come first, followed by a number. Compound expressions are not allowed, but the filter may be chained multiple times.<br />
<br />
A decimal point in the input or argument will force conversion of both to a double float. Integer division is performed on integers, floating point division on floats.<br />
<br />
Comparisons evaluate to null when false, or the string "TRUE" when true, which is expected by "if" expressions, eg:<br />
<br />
<code>{% if $n|comp(>30) %}...{% endif %}</code><br />
<br />
NB: When using the comp filter for comparisons in a standalone tag, you must provide a default "FALSE" value to avoid triggering tag pass-through preservation:<br />
<br />
<code>{% $n|comp(>30):FALSE %}</code> evaluates to <code>TRUE</code> or <code>FALSE</code><br />
<br />
<code>{% $n|comp(>30) %}</code> evaluates to <code>TRUE</code> or <code>{% $n|comp(>30) %}</code><br />
<br />
Aliases: qcalc, qc<br/><br />
Versions: 3.1.1+<br />
<br />
== defang ==<br />
Remove all but a short list of "harmless" characters - can apply to values from untrusted sources to neuter potential XSS attacks.<br />
<br />
Alias: neuter<br />
<br />
== default(<output>) ==<br />
<br />
Output <output> if the $tag is not found in the tag table. If the tag is found, the tag value is _not_ consulted. Empty string, or a string that is completely whitespace will be output and will _not_ trigger the default output. v2.5 and up. See [[#onempty.28.3Coutput.3E.29|onempty]].<br />
<br />
== escapequotes ==<br />
Escape single and double quotes (and backslashes) with a backslash.<br />
<br />
Aliases: qs, quoted, quotedstring<br />
<br />
== escapexml ==<br />
Convert &amp; to &amp;amp; and handle angle brackets, single and double quotes, etc. See [[#unescapexml|unescapexml]].<br />
<br />
Aliases: xml, html, xmlescape, htmlescape<br />
<br />
== filter(<#xtemplate>) ==<br />
Bind the tag value to {$x} and include the specified template. Provides a way to write user-contributed filters using template tag language.<br />
<br />
== get(<index>) ==<br />
Output the LIST element at <index>. Zero (0) is the first element and -1 is the last element. Negative indices count back from the end of the LIST.<br />
<br />
== indent(<howmany>[,indentstring]) ==<br />
Indent each line of the input by <howmany>. Default indentstring is a single space.<br />
<br />
== join[(<delimiter>)] ==<br />
Join the LIST elements into a STRING, optionally delimited by <delimiter>. Null input results in null output (3.6.0+).<br />
<br />
== length ==<br />
Outputs the size of a LIST or OBJECT, or the length of a STRING input.<br />
<br />
Alias: len<br />
<br />
== lower ==<br />
Convert text to lowercase. Honors Java locale settings.<br />
<br />
Alias: lc<br />
<br />
== lpad[(<prefix>[,<howmany>])] ==<br />
If tag is defined and non-empty, output with prefix (default prefix is a single space " ").<br />
<br />
== md5hex ==<br />
MD5-hash (bytes encoded in two-character hex format)<br />
<br />
Alias: md5<br />
<br />
== ondefined(<output>) ==<br />
Output <output> when tag value is defined. White space or the empty string is not considered defined for this test.<br />
<br />
== onempty(<output>) ==<br />
For undefined _or_ "empty" values (ie empty string or only whitespace), output the given <output>, otherwise let the tag value pass through. See [[#default.28.3Coutput.3E.29|default]].<br />
<br />
== page(<pagenum>[,<pagesize>]) ==<br />
Convenience filter for slicing one "page" of a LIST. First page is pagenum=1, not pagenum=0. The pagenum argument may be a positive integer or a $tag that resolves to a positive integer.<br />
<br />
Default page size is 10.<br />
<br />
Versions: 3.1.3+<br />
<br />
== reverse ==<br />
Reverse a LIST.<br />
<br />
== rpad[(<suffix>[,<howmany>])] ==<br />
If tag is defined and non-empty, output with suffix (default suffix is a single space " ").<br />
<br />
== s/<regex>/<replace>/[flags] ==<br />
Perl-style regular expression search and replace.<br />
<br />
== selected(<testvalue>[,<output>]) ==<br />
Output selected="selected" (or <output> if specified) when tag value matches <testvalue>. Argument may be an unbraced $tag.<br />
<br />
Aliases: select, sel<br />
<br />
== sha1hex ==<br />
SHA-1 hash (bytes encoded in two-character hex format).<br />
<br />
Alias: sha1<br />
<br />
== slice([from][:to][:step]) ==<br />
Subset a LIST using python-style slice notation. Negative indices and steps are supported. Default values for <from> and <to> are the beginning and end of the list. If input is null, output will be null (3.6.0+). Works on strings too (treats string as list of characters). May separate args with comma instead of colon if desired.<br />
<br />
== sort ==<br />
Sort a LIST of homogenous, comparable items.<br />
<br />
== split[(<delimiter>|/<regexdelim>/[,<limit>])] ==<br />
Transform delimited STRING into a LIST, using a simple delimiter or a /regex/ delimiter. Default delimiter is whitespace. <limit> arg is also optional.<br />
<br />
== sprintf(<format>) ==<br />
The classic formatter. See [[http://www.x5software.com/chunk/examples/ChunkExample?example=sprintf sprintf examples]].<br />
<br />
== string ==<br />
The string representation of an object. Calls the object's .toString() method. Only useful in rare cases (eg when chaining filters) since this mirrors the default rendering behavior.<br />
<br />
Alias: str<br />
<br />
== th ==<br />
English ordinal suffix (1 -&gt; 1st, 2 -&gt; 2nd, 10 -&gt; 10th etc.).<br />
<br />
Alias: ordsuffix<br />
<br />
== title ==<br />
Uppercase the first letter of each word, and lowercase all other characters. See [[#capitalize|capitalize]]. Does not follow any rules about not title-casing small words.<br />
<br />
== translate ==<br />
See [[Chunk_Documentation#Chunk_.5BLocalization.5D|complete localization documentation]] to learn how to use this feature.<br />
<br />
Aliases: xlate, _<br />
<br />
== trim ==<br />
Trim leading and trailing whitespace.<br />
<br />
== type ==<br />
Output STRING, LIST, OBJECT, CHUNK or NULL per the tag value type.<br />
<br />
== unescapexml ==<br />
Unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB; See [[#escapexml|escapexml]].<br />
<br />
Aliases: unescapehtml, unxml, unhtml, xmlunescape<br />
<br />
== upper ==<br />
Convert text to uppercase. Honors Java locale settings.<br />
<br />
Alias: uc<br />
<br />
== urldecode ==<br />
Restore %AB encoded characters. Plus character (+) is translated back to space.<br />
<br />
== urlencode ==<br />
URL-Encode the value.<br />
<br />
Alias: url</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_Tag_FiltersChunk Tag Filters2021-03-22T04:16:59Z<p>Tom McClure: /* slice([from]:[to][:step]) */</p>
<hr />
<div>Chunk includes several useful filters right out of the box, eg <code>{$tag|upper}</code>.<br />
<br />
Filters may be chained ad infinitum: <code>{$tag|md5hex|upper}</code>.<br />
<br />
== alternate(<evenoutput>,<oddoutput>) ==<br />
When tag value is even, output <evenoutput> - when odd, output <oddoutput>. Unbraced tags are valid arguments, eg:<br />
<br />
output the strings "even" and "odd":<br />
{% $i|alternate(even,odd) %}<br />
<br />
output {$even} and {$odd} tags:<br />
{% $i|alternate($even,$odd) %}<br />
<br />
include templates {#even} and {#odd}<br />
{% $i|alternate(+#even,+#odd) %}<br />
<br />
Alias: evenodd<br />
<br />
== base64 ==<br />
Base64-encode.<br />
<br />
== base64decode ==<br />
Base64-decode.<br />
<br />
== bool ==<br />
Translates "truthy" values to "TRUE"/null for use in "if" expressions.<br />
<br />
eg: {% if ($n|calc($x * $x > 20)|bool) %} ... {% endif %}<br />
<br />
<table><br />
<tr><br />
<th></th><br />
<th align="left">Input</th><br />
<th align="left">Output</th><br />
</tr><br />
<tr><br />
<td></td><br />
<td>null</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td align="right">empty string:&nbsp;</td><br />
<td>""</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td align="right">whitespace-only string:&nbsp;</td><br />
<td>" "</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td align="right">any defined, non-empty string:&nbsp;</td><br />
<td>"bananas"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"0"</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"1"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"2"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"-1"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"0.0"</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"1.0"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"21.0"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"-1.0"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"true"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"false"</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"True"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"False"</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"TRUE"</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>"FALSE"</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>non-null object</td><br />
<td>TRUE</td><br />
</tr><br />
<tr><br />
<td></td><br />
<td>null object</td><br />
<td>null</td><br />
</tr><br />
<tr><br />
<td align="right">the string "null":&nbsp;</td><br />
<td>"null"</td><br />
<td>TRUE</td><br />
</tr><br />
</table><br />
<br />
Alias: boolean<br />
<br />
== calc(<expr>[,<fmt>]) ==<br />
Requires jeplite - see [https://github.com/tomj74/chunk-templates GitHub project page] for download instructions.<br />
<br />
The calc filter is similar to the comp filter but provides much richer expression parsing and simple $tag references.<br />
<br />
If $x appears in <expr> then the input value is substituted for $x, otherwise the input value is prepended to the expression.<br />
<br />
The input may be filtered before passing to the calc filter, but direct $tag references in the expression may not use any filter/default modifiers.<br />
<br />
Examples:<br />
<br />
{% $n:2|calc(+2) %}<br/><br />
output: 4.0<br />
<br />
{% $n:2|calc("$x+$x") %}<br/><br />
output: 4.0<br />
<br />
{% $n:0.5|calc("sin(pi*$x)","%.2f") %}<br/><br />
output: 1.00<br />
<br />
{% $a|calc("(-$b + sqrt($b^2 - 4*$x*$c))/(2*$x)") %}<br/><br />
output: well, it depends on values of $a $b $c ...<br />
<br />
{{blockquote|<br />
'''Notes'''<br />
* Non-numeric input (including null/undefined) will pass through the filter with no processing and the expression will not be executed. <br />
* Behavior when referencing non-numeric tag values in expressions is undefined<br />
* Functions available: sin cos tan asin acos atan sqrt log ln angle abs mod sum rand umin add<br />
* Computational operators: * / + - ^ %<br />
* Comparison operators: > < >&#61; <&#61; &#61;&#61; !&#61; <><br />
* Grouping operators: ( )<br />
* Constants available: e and pi<br />
}}<br />
<br />
Jeplite evaluates comparisons as 0.0 for false and 1.0 for true, so that output must be passed through the "bool" filter if you wish to use it in an "if" expression.<br />
<br />
== capitalize ==<br />
Uppercase the first letter of each word. Leave case of other characters intact. See [[#title|title]].<br />
<br />
Alias: cap<br />
<br />
== checked(<testvalue>[,<output>]) ==<br />
<br />
Output checked="checked" (or <output> if specified) when tag value matches <testvalue>. Argument may be an unbraced $tag.<br />
<br />
== comp(<operation>) ==<br />
Perform a limited set of comparisons (> < >= <= <> == !=) or mathematical transformations (+, -, *, /, %, ^) on an integer (long) or floating-point (decimal) input. eg: <code>{% $tag|qcalc(+20) %}</code>.<br />
<br />
The <operation> argument syntax is very strict: an operator symbol must come first, followed by a number. Compound expressions are not allowed, but the filter may be chained multiple times.<br />
<br />
A decimal point in the input or argument will force conversion of both to a double float. Integer division is performed on integers, floating point division on floats.<br />
<br />
Comparisons evaluate to null when false, or the string "TRUE" when true, which is expected by "if" expressions, eg:<br />
<br />
<code>{% if $n|comp(>30) %}...{% endif %}</code><br />
<br />
NB: When using the comp filter for comparisons in a standalone tag, you must provide a default "FALSE" value to avoid triggering tag pass-through preservation:<br />
<br />
<code>{% $n|comp(>30):FALSE %}</code> evaluates to <code>TRUE</code> or <code>FALSE</code><br />
<br />
<code>{% $n|comp(>30) %}</code> evaluates to <code>TRUE</code> or <code>{% $n|comp(>30) %}</code><br />
<br />
Aliases: qcalc, qc<br/><br />
Versions: 3.1.1+<br />
<br />
== defang ==<br />
Remove all but a short list of "harmless" characters - can apply to values from untrusted sources to neuter potential XSS attacks.<br />
<br />
Alias: neuter<br />
<br />
== default(<output>) ==<br />
<br />
Output <output> if the $tag is not found in the tag table. If the tag is found, the tag value is _not_ consulted. Empty string, or a string that is completely whitespace will be output and will _not_ trigger the default output. v2.5 and up. See [[#onempty.28.3Coutput.3E.29|onempty]].<br />
<br />
== escapequotes ==<br />
Escape single and double quotes (and backslashes) with a backslash.<br />
<br />
Aliases: qs, quoted, quotedstring<br />
<br />
== escapexml ==<br />
Convert &amp; to &amp;amp; and handle angle brackets, single and double quotes, etc. See [[#unescapexml|unescapexml]].<br />
<br />
Aliases: xml, html, xmlescape, htmlescape<br />
<br />
== filter(<#xtemplate>) ==<br />
Bind the tag value to {$x} and include the specified template. Provides a way to write user-contributed filters using template tag language.<br />
<br />
== get(<index>) ==<br />
Output the LIST element at <index>. Zero (0) is the first element and -1 is the last element. Negative indices count back from the end of the LIST.<br />
<br />
== indent(<howmany>[,indentstring]) ==<br />
Indent each line of the input by <howmany>. Default indentstring is a single space.<br />
<br />
== join[(<delimiter>)] ==<br />
Join the LIST elements into a STRING, optionally delimited by <delimiter>.<br />
<br />
== length ==<br />
Outputs the size of a LIST or OBJECT, or the length of a STRING input.<br />
<br />
Alias: len<br />
<br />
== lower ==<br />
Convert text to lowercase. Honors Java locale settings.<br />
<br />
Alias: lc<br />
<br />
== lpad[(<prefix>[,<howmany>])] ==<br />
If tag is defined and non-empty, output with prefix (default prefix is a single space " ").<br />
<br />
== md5hex ==<br />
MD5-hash (bytes encoded in two-character hex format)<br />
<br />
Alias: md5<br />
<br />
== ondefined(<output>) ==<br />
Output <output> when tag value is defined. White space or the empty string is not considered defined for this test.<br />
<br />
== onempty(<output>) ==<br />
For undefined _or_ "empty" values (ie empty string or only whitespace), output the given <output>, otherwise let the tag value pass through. See [[#default.28.3Coutput.3E.29|default]].<br />
<br />
== page(<pagenum>[,<pagesize>]) ==<br />
Convenience filter for slicing one "page" of a LIST. First page is pagenum=1, not pagenum=0. The pagenum argument may be a positive integer or a $tag that resolves to a positive integer.<br />
<br />
Default page size is 10.<br />
<br />
Versions: 3.1.3+<br />
<br />
== reverse ==<br />
Reverse a LIST.<br />
<br />
== rpad[(<suffix>[,<howmany>])] ==<br />
If tag is defined and non-empty, output with suffix (default suffix is a single space " ").<br />
<br />
== s/<regex>/<replace>/[flags] ==<br />
Perl-style regular expression search and replace.<br />
<br />
== selected(<testvalue>[,<output>]) ==<br />
Output selected="selected" (or <output> if specified) when tag value matches <testvalue>. Argument may be an unbraced $tag.<br />
<br />
Aliases: select, sel<br />
<br />
== sha1hex ==<br />
SHA-1 hash (bytes encoded in two-character hex format).<br />
<br />
Alias: sha1<br />
<br />
== slice([from][:to][:step]) ==<br />
Subset a LIST using python-style slice notation. Negative indices and steps are supported. Default values for <from> and <to> are the beginning and end of the list. If input is null, output will be null (3.6.0+). Works on strings too (treats string as list of characters). May separate args with comma instead of colon if desired.<br />
<br />
== sort ==<br />
Sort a LIST of homogenous, comparable items.<br />
<br />
== split[(<delimiter>|/<regexdelim>/[,<limit>])] ==<br />
Transform delimited STRING into a LIST, using a simple delimiter or a /regex/ delimiter. Default delimiter is whitespace. <limit> arg is also optional.<br />
<br />
== sprintf(<format>) ==<br />
The classic formatter. See [[http://www.x5software.com/chunk/examples/ChunkExample?example=sprintf sprintf examples]].<br />
<br />
== string ==<br />
The string representation of an object. Calls the object's .toString() method. Only useful in rare cases (eg when chaining filters) since this mirrors the default rendering behavior.<br />
<br />
Alias: str<br />
<br />
== th ==<br />
English ordinal suffix (1 -&gt; 1st, 2 -&gt; 2nd, 10 -&gt; 10th etc.).<br />
<br />
Alias: ordsuffix<br />
<br />
== title ==<br />
Uppercase the first letter of each word, and lowercase all other characters. See [[#capitalize|capitalize]]. Does not follow any rules about not title-casing small words.<br />
<br />
== translate ==<br />
See [[Chunk_Documentation#Chunk_.5BLocalization.5D|complete localization documentation]] to learn how to use this feature.<br />
<br />
Aliases: xlate, _<br />
<br />
== trim ==<br />
Trim leading and trailing whitespace.<br />
<br />
== type ==<br />
Output STRING, LIST, OBJECT, CHUNK or NULL per the tag value type.<br />
<br />
== unescapexml ==<br />
Unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB; See [[#escapexml|escapexml]].<br />
<br />
Aliases: unescapehtml, unxml, unhtml, xmlunescape<br />
<br />
== upper ==<br />
Convert text to uppercase. Honors Java locale settings.<br />
<br />
Alias: uc<br />
<br />
== urldecode ==<br />
Restore %AB encoded characters. Plus character (+) is translated back to space.<br />
<br />
== urlencode ==<br />
URL-Encode the value.<br />
<br />
Alias: url</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2021-03-22T04:13:08Z<p>Tom McClure: /* Chunk _[Localization] */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.6.0</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
{{blockquote|<br />
'''Missing Templates'''<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
If you don't want these errors to pass silently, you can call <code>theme.setAbortOnMissingTemplate(true)</code> and the engine will instead throw a TemplateNotFoundException.<br />
}}<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
Make sure your locale folder contains multiple folders named for each locale code, with a csv file named translate.csv in each one.<br />
<br />
locale/<br />
|-- de_DE/<br />
| +-- translate.csv<br />
|-- es_ES/<br />
| +-- translate.csv<br />
+-- fr_FR/<br />
+-- translate.csv<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the "translate" filter, like so:<br />
<br />
{$tag|translate}<br />
<br />
or use these shortcut aliases "xlate" or "_" like so:<br />
<br />
{$tag|xlate} {$tag|_}<br />
<br />
=== Custom Locale files path ===<br />
<br />
If you want to place translation csv files on the filesystem and not as resources in the classpath, set the System property chunk.localedb.path to the location of the "locale" folder.<br />
<br />
=== Custom TranslationsProvider ===<br />
<br />
Some deployment environments (for example, Android) have neither access to the filesystem nor access to resources from the classpath.<br />
<br />
You can write your own class which implements com.x5.template.providers.TranslationsProvider<br />
and pass it to the Theme object:<br />
<br />
class MyTranslationsProvider implements TranslationsProvider {<br />
public Map<String, String> getTranslations(String localeCode) {<br />
// ... load translations from app context assets? ...<br />
}<br />
}<br />
<br />
// ...<br />
Theme theme = new Theme();<br />
theme.setTranslationsProvider(new MyTranslationsProvider());<br />
theme.setLocale("fr_FR");<br />
Chunk chunk = theme.makeChunk()<br />
// ...<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2021-03-22T04:11:41Z<p>Tom McClure: /* Chunk _[Localization] */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.6.0</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
{{blockquote|<br />
'''Missing Templates'''<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
If you don't want these errors to pass silently, you can call <code>theme.setAbortOnMissingTemplate(true)</code> and the engine will instead throw a TemplateNotFoundException.<br />
}}<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
Make sure your locale folder contains multiple folders named for each locale code, with a csv file named translate.csv in each one.<br />
<br />
locale<br />
|-- de_DE<br />
| +-- translate.csv<br />
|-- es_ES<br />
| +-- translate.csv<br />
+-- fr_FR<br />
+-- translate.csv<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the "translate" filter, like so:<br />
<br />
{$tag|translate}<br />
<br />
or use these shortcut aliases "xlate" or "_" like so:<br />
<br />
{$tag|xlate} {$tag|_}<br />
<br />
=== Custom Locale files path ===<br />
<br />
If you want to place translation csv files on the filesystem and not as resources in the classpath, set the System property chunk.localedb.path to the location of the "locale" folder.<br />
<br />
=== Custom TranslationsProvider ===<br />
<br />
Some deployment environments (for example, Android) have neither access to the filesystem nor access to resources from the classpath.<br />
<br />
You can write your own class which implements com.x5.template.providers.TranslationsProvider<br />
and pass it to the Theme object:<br />
<br />
class MyTranslationsProvider implements TranslationsProvider {<br />
public Map<String, String> getTranslations(String localeCode) {<br />
// ... load translations from app context assets? ...<br />
}<br />
}<br />
<br />
// ...<br />
Theme theme = new Theme();<br />
theme.setTranslationsProvider(new MyTranslationsProvider());<br />
theme.setLocale("fr_FR");<br />
Chunk chunk = theme.makeChunk()<br />
// ...<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2021-03-22T04:04:53Z<p>Tom McClure: /* Advanced usage, filter tag */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.6.0</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
{{blockquote|<br />
'''Missing Templates'''<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
If you don't want these errors to pass silently, you can call <code>theme.setAbortOnMissingTemplate(true)</code> and the engine will instead throw a TemplateNotFoundException.<br />
}}<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the "translate" filter, like so:<br />
<br />
{$tag|translate}<br />
<br />
or use these shortcut aliases "xlate" or "_" like so:<br />
<br />
{$tag|xlate} {$tag|_}<br />
<br />
=== Custom Locale files path ===<br />
<br />
If you want to place translation csv files on the filesystem and not as resources in the classpath, set the System property chunk.localedb.path to the location of the "locale" folder.<br />
<br />
=== Custom TranslationsProvider ===<br />
<br />
Some deployment environments (for example, Android) have neither access to the filesystem nor access to resources from the classpath.<br />
<br />
You can write your own class which implements com.x5.template.providers.TranslationsProvider<br />
and pass it to the Theme object:<br />
<br />
class MyTranslationsProvider implements TranslationsProvider {<br />
public Map<String, String> getTranslations(String localeCode) {<br />
// ... load translations from app context assets? ...<br />
}<br />
}<br />
<br />
// ...<br />
Theme theme = new Theme();<br />
theme.setTranslationsProvider(new MyTranslationsProvider());<br />
theme.setLocale("fr_FR");<br />
Chunk chunk = theme.makeChunk()<br />
// ...<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2021-03-22T03:50:47Z<p>Tom McClure: /* How to get Chunk */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.6.0</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
{{blockquote|<br />
'''Missing Templates'''<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
If you don't want these errors to pass silently, you can call <code>theme.setAbortOnMissingTemplate(true)</code> and the engine will instead throw a TemplateNotFoundException.<br />
}}<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2019-01-12T22:02:21Z<p>Tom McClure: /* Leave out the extension! */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
{{blockquote|<br />
'''Missing Templates'''<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
If you don't want these errors to pass silently, you can call <code>theme.setAbortOnMissingTemplate(true)</code> and the engine will instead throw a TemplateNotFoundException.<br />
}}<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2019-01-12T21:59:35Z<p>Tom McClure: /* Leave out the extension! */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
If you don't want these errors to pass silently, you can call <code>theme.setAbortOnMissingTemplate(true)</code> and the engine will instead throw a TemplateNotFoundException.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2019-01-12T21:58:56Z<p>Tom McClure: /* Leave out the extension! */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
If you don't want these errors to pass silently, you can call theme.setAbortOnMissingTemplate(true) and the engine will instead throw a TemplateNotFoundException.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2018-11-30T18:52:32Z<p>Tom McClure: /* POJO or Bean */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2018-11-30T15:05:00Z<p>Tom McClure: /* POJO or Bean */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
Bean methods like getFullName() become object properties in the template, with the "get" prefix stripped. Additionally, all properties undergo a CamelCase to lower_with_underscores conversion. For example:<br />
<br />
* getFullName() => {$x.full_name}<br />
* getPriceUSD() => {$x.price_usd}<br />
* getTotalJellyBeans() => {$x.total_jelly_beans}<br />
* isActive() => {$x.is_active}<br />
* hasLemons() => {$x.has_lemons}<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2018-10-22T04:46:43Z<p>Tom McClure: /* POJO or Bean */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean annotations from the com.x5.util package (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2018-10-22T04:45:28Z<p>Tom McClure: /* Simple Binding with POJO/Bean */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== POJO or Bean ====<br />
<br />
Chunk uses reflection to check for non-private/protected fields. If it finds any, it will treat the class as a POJO and expose fields to the template, otherwise it assumes that it is dealing with a bean and stores the return values of the "getter" functions at render time in a map, which is passed to the template.<br />
<br />
If you wish to force a class to be treated as a POJO or Bean, annotate your class with @AccessAsPojo or @AccessAsBean (new in 3.4.0).<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2018-10-22T04:38:27Z<p>Tom McClure: /* Introduction */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.4.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2017-10-23T23:38:40Z<p>Tom McClure: /* Getting Started: Creating HTML with Chunk */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2017-10-23T23:37:40Z<p>Tom McClure: /* Introduction */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure - web developer, [https://soundcloud.com/tommysasso musician] and dad. My day job is maintaining part of the stack behind [http://pandora.com/ Pandora]. Read my [http://www.dagblastit.com/blog blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2017-09-04T14:48:23Z<p>Tom McClure: /* Chunk Tag :Defaults */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both of the examples above, the "else" clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2017-09-04T14:47:48Z<p>Tom McClure: /* Chunk Tag :Defaults */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined and will not trigger the default output. If you do want to trigger a default on empty string also, try the "|onempty(...)" filter.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2017-09-03T15:50:49Z<p>Tom McClure: /* loop */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
loop tag options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2017-09-03T14:40:34Z<p>Tom McClure: /* if */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are evaluated for true/false, '''all value comparisons are string comparisons only''', never numeric equivalence nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so they will be interpreted as expected in a simple if-test for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2017-09-03T14:40:12Z<p>Tom McClure: /* if */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Starting in 3.3.0, Chunk's if-expressions now permit compound logic with grouping and the && and || operators.<br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Tag values are '''coerced to strings''' for template output in an earlier step. This means that when if-expressions are<br />
evaluated for true/false, '''all value comparisons are<br />
string comparisons only''', never numeric equivalence<br />
nor any notion of java-style equality.<br />
<br />
Booleans map to the string "TRUE" or null/undefined, so<br />
they will be interpreted as expected in a simple if-test<br />
for existence.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2017-09-02T03:01:44Z<p>Tom McClure: /* Contribute a filter */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args.getUnparsedArgs() will contain the entire argument string "3,*" but if your arguments are comma-delimited, you'd probably prefer not to parse them yourself.<br />
<br />
args.getFilterArgs(chunk) (3.3.0+) will return the filter arguments as an array of strings (context-interpolated if the args start with $). In this example, the args to indent are returned as the array {"3","*"} -- the optional second arg to indent is the string to use for indenting - default is a single space " ".<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2017-09-02T02:47:50Z<p>Tom McClure: /* Contribute a filter */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, FilterArgs args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2017-08-29T23:44:30Z<p>Tom McClure: /* Contribute a filter */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
TODO: update to new 3.x method signature for transformText (uses FilterArgs object)<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2017-08-29T21:55:43Z<p>Tom McClure: /* Backticks (dynamic tags) */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, parameter or filter arguments, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2017-06-21T14:54:25Z<p>Tom McClure: /* Inherit root template with {% super %} */</p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All <code>{% include file#snippet %}</code> references will function as intended, but a theme reference to the bare <code>{% include file %}</code> will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-12-16T07:26:48Z<p>Tom McClure: </p>
<hr />
<div>== How to get Chunk ==<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
== What is Chunk ==<br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-12-16T07:25:38Z<p>Tom McClure: </p>
<hr />
<div>Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-12-16T07:25:26Z<p>Tom McClure: </p>
<hr />
<div>Welcome to the Chunk Templates Wiki..<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Spring_MVCSpring MVC2016-12-16T07:18:53Z<p>Tom McClure: /* Can I use Chunk Templates in my Spring MVC project? */</p>
<hr />
<div>== Can I use Chunk Templates in my Spring MVC project? ==<br />
<br />
Yes!<br />
<br />
Since v2.6.4 Chunk has had bean-friendly plumbing that makes it easy to use with [http://projects.spring.io/spring-framework/ Spring MVC], as a drop-in replacement template engine for Freemarker or Velocity.<br />
<br />
== Special features ==<br />
<br />
MVC Framework localized messages (defined in messages.properties etc) are available via a custom tag command:<br />
<br />
{% messages.msg.name %}<br />
<br />
Messages can be parameterized like so:<br />
<br />
{% messages.msg.name(`$a`,`$b`) %}<br />
<br />
A special request context tag is available, usually {$rc} but the name is configurable.<br />
<br />
The following request context values are available:<br />
<br />
{$rc.uri}<br />
{$rc.context_path}<br />
{$rc.servlet_path}<br />
{$rc.scheme}<br />
{$rc.method}<br />
{$rc.server_name}<br />
{$rc.remote_addr}<br />
{$rc.remote_host}<br />
{$rc.remote_user}<br />
<br />
== Using bean getters in a template ==<br />
<br />
When exposing beans (or POJO member variables) in a model, keep in mind that Chunk always converts camelCase to snake_case.<br />
<br />
So, <code>x.getFavoriteColor()</code> will be available in the template as <code>{% $x.favorite_color %}</code> -- and <code>x.isBigAndHairy()</code> must be checked like so:<br />
<br />
{% if $x.big_and_hairy %}...{% endif %}<br />
<br />
== Is there a sample project that I can clone? ==<br />
<br />
Why yes, just head here: [https://github.com/tomj74/chunk-spring-quickstart Sample Spring MVC project on GitHub]<br />
<br />
You'll be up and running in no time.<br />
<br />
== Getting Started ==<br />
<br />
Here's what you will need:<br />
<br />
1. pom.xml dependencies:<br />
<br />
...<br />
<dependencies><br />
'''<dependency>'''<br />
'''<groupId>com.x5dev</groupId>'''<br />
'''<artifactId>chunk-springmvc</artifactId>'''<br />
'''<version>0.1.0</version>'''<br />
'''</dependency>'''<br />
'''<dependency>'''<br />
'''<groupId>com.x5dev</groupId>'''<br />
'''<artifactId>chunk-templates</artifactId>'''<br />
'''<version>3.2.4</version>'''<br />
'''</dependency>'''<br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-context</artifactId><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-aop</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-webmvc</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-web</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>jstl</groupId><br />
<artifactId>jstl</artifactId><br />
<version>1.2</version><br />
</dependency><br />
<dependency><br />
<groupId>commons-logging</groupId><br />
<artifactId>commons-logging</artifactId><br />
<version>1.2</version><br />
</dependency><br />
</dependencies><br />
<dependencyManagement><br />
<dependencies><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-context</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
</dependencies><br />
</dependencyManagement><br />
...<br />
<br />
2. WebContent/WEB-INF/web.xml<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<br />
<web-app version="2.4"<br />
xmlns="http://java.sun.com/xml/ns/j2ee"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee <br />
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" ><br />
<br />
<display-name>Chunky Spring</display-name><br />
<br />
<servlet><br />
<servlet-name>dispatcher</servlet-name><br />
<servlet-class><br />
org.springframework.web.servlet.DispatcherServlet<br />
</servlet-class><br />
<load-on-startup>1</load-on-startup><br />
</servlet><br />
<br />
<servlet-mapping><br />
<servlet-name>dispatcher</servlet-name><br />
<url-pattern>/</url-pattern><br />
</servlet-mapping><br />
<br />
<context-param><br />
<param-name>contextConfigLocation</param-name><br />
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value><br />
</context-param><br />
<br />
<listener><br />
<listener-class><br />
org.springframework.web.context.ContextLoaderListener<br />
</listener-class><br />
</listener><br />
<br />
</web-app><br />
<br />
3. WebContent/WEB-INF/dispatcher-servlet.xml - provide some Theme configuration and drop in a custom view class (source below) to viewResolver.<br />
<br />
Make sure to change <code>com.example.myapp</code> to the package where your annotated controllers live.<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<beans xmlns="http://www.springframework.org/schema/beans"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"<br />
xmlns:context="http://www.springframework.org/schema/context"<br />
xsi:schemaLocation="http://www.springframework.org/schema/beans<br />
<br />
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd<br />
http://www.springframework.org/schema/context <br />
http://www.springframework.org/schema/context/spring-context-4.0.xsd"><br />
<br />
<context:component-scan base-package="'''com.example.myapp'''" /><br />
<br />
<bean id="chunkTemplatesConfig" class="java.util.HashMap" scope="prototype"><br />
<constructor-arg><br />
<map key-type="java.lang.String" value-type="java.lang.String"><br />
<entry key="default_extension" value="chtml" /><br />
<entry key="cache_minutes" value="0" /><br />
<entry key="layers" value="" /><br />
<entry key="theme_path" value="" /><br />
<entry key="hide_errors" value="FALSE" /><br />
<entry key="error_log" value="" /><br />
<entry key="encoding" value="UTF-8" /><br />
<entry key="locale" value="" /><br />
<entry key="filters" value="" /><br />
</map><br />
</constructor-arg><br />
</bean><br />
<br />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><br />
<property name="viewClass" value="com.x5.template.spring.ChunkTemplateView"/><br />
<property name="prefix" value="/WEB-INF/themes/"/><br />
<property name="suffix" value=".chtml"/><br />
<property name="requestContextAttribute" value="rc"/><br />
</bean><br />
</beans><br />
<br />
4. Make a controller! The template name "helloworld" will resolve to /WEB-INF/themes/helloworld.chtml and any include/exec calls will be resolved relative to there.<br />
<br />
package com.example.myapp;<br />
<br />
import org.springframework.stereotype.Controller;<br />
import org.springframework.ui.Model;<br />
import org.springframework.web.bind.annotation.RequestMapping;<br />
import org.springframework.web.bind.annotation.RequestParam;<br />
import org.springframework.web.servlet.ModelAndView;<br />
<br />
@Controller<br />
public class HelloChunk<br />
{<br />
String message = "Welcome to Spring MVC!";<br />
<br />
@RequestMapping("/hello")<br />
public ModelAndView showMessage(<br />
@RequestParam(value = "name", required = false, defaultValue = "World") String name)<br />
{<br />
ModelAndView mv = new ModelAndView("helloworld");<br />
mv.addObject("message", message);<br />
mv.addObject("name", name);<br />
mv.addObject("page_name", "Spring 4 MVC - Hello Chunk");<br />
return mv;<br />
}<br />
<br />
@RequestMapping(value={"/chunk","/"})<br />
public ModelAndView chunkTime()<br />
{<br />
ModelAndView mv = new ModelAndView("chunkworld");<br />
mv.addObject("page_name", "Chunky Time");<br />
return mv;<br />
}<br />
}</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Spring_MVCSpring MVC2016-12-16T07:02:27Z<p>Tom McClure: </p>
<hr />
<div>== Can I use Chunk Templates in my Spring MVC project? ==<br />
<br />
Yes!<br />
<br />
Chunk 2.6.4 adds some new plumbing that makes it easy to use with [http://projects.spring.io/spring-framework/ Spring MVC], as a drop-in replacement template engine for Freemarker or Velocity.<br />
<br />
== Special features ==<br />
<br />
MVC Framework localized messages (defined in messages.properties etc) are available via a custom tag command:<br />
<br />
{% messages.msg.name %}<br />
<br />
Messages can be parameterized like so:<br />
<br />
{% messages.msg.name(`$a`,`$b`) %}<br />
<br />
A special request context tag is available, usually {$rc} but the name is configurable.<br />
<br />
The following request context values are available:<br />
<br />
{$rc.uri}<br />
{$rc.context_path}<br />
{$rc.servlet_path}<br />
{$rc.scheme}<br />
{$rc.method}<br />
{$rc.server_name}<br />
{$rc.remote_addr}<br />
{$rc.remote_host}<br />
{$rc.remote_user}<br />
<br />
== Using bean getters in a template ==<br />
<br />
When exposing beans (or POJO member variables) in a model, keep in mind that Chunk always converts camelCase to snake_case.<br />
<br />
So, <code>x.getFavoriteColor()</code> will be available in the template as <code>{% $x.favorite_color %}</code> -- and <code>x.isBigAndHairy()</code> must be checked like so:<br />
<br />
{% if $x.big_and_hairy %}...{% endif %}<br />
<br />
== Is there a sample project that I can clone? ==<br />
<br />
Why yes, just head here: [https://github.com/tomj74/chunk-spring-quickstart Sample Spring MVC project on GitHub]<br />
<br />
You'll be up and running in no time.<br />
<br />
== Getting Started ==<br />
<br />
Here's what you will need:<br />
<br />
1. pom.xml dependencies:<br />
<br />
...<br />
<dependencies><br />
'''<dependency>'''<br />
'''<groupId>com.x5dev</groupId>'''<br />
'''<artifactId>chunk-springmvc</artifactId>'''<br />
'''<version>0.1.0</version>'''<br />
'''</dependency>'''<br />
'''<dependency>'''<br />
'''<groupId>com.x5dev</groupId>'''<br />
'''<artifactId>chunk-templates</artifactId>'''<br />
'''<version>3.2.4</version>'''<br />
'''</dependency>'''<br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-context</artifactId><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-aop</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-webmvc</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-web</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>jstl</groupId><br />
<artifactId>jstl</artifactId><br />
<version>1.2</version><br />
</dependency><br />
<dependency><br />
<groupId>commons-logging</groupId><br />
<artifactId>commons-logging</artifactId><br />
<version>1.2</version><br />
</dependency><br />
</dependencies><br />
<dependencyManagement><br />
<dependencies><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-context</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
</dependencies><br />
</dependencyManagement><br />
...<br />
<br />
2. WebContent/WEB-INF/web.xml<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<br />
<web-app version="2.4"<br />
xmlns="http://java.sun.com/xml/ns/j2ee"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee <br />
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" ><br />
<br />
<display-name>Chunky Spring</display-name><br />
<br />
<servlet><br />
<servlet-name>dispatcher</servlet-name><br />
<servlet-class><br />
org.springframework.web.servlet.DispatcherServlet<br />
</servlet-class><br />
<load-on-startup>1</load-on-startup><br />
</servlet><br />
<br />
<servlet-mapping><br />
<servlet-name>dispatcher</servlet-name><br />
<url-pattern>/</url-pattern><br />
</servlet-mapping><br />
<br />
<context-param><br />
<param-name>contextConfigLocation</param-name><br />
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value><br />
</context-param><br />
<br />
<listener><br />
<listener-class><br />
org.springframework.web.context.ContextLoaderListener<br />
</listener-class><br />
</listener><br />
<br />
</web-app><br />
<br />
3. WebContent/WEB-INF/dispatcher-servlet.xml - provide some Theme configuration and drop in a custom view class (source below) to viewResolver.<br />
<br />
Make sure to change <code>com.example.myapp</code> to the package where your annotated controllers live.<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<beans xmlns="http://www.springframework.org/schema/beans"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"<br />
xmlns:context="http://www.springframework.org/schema/context"<br />
xsi:schemaLocation="http://www.springframework.org/schema/beans<br />
<br />
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd<br />
http://www.springframework.org/schema/context <br />
http://www.springframework.org/schema/context/spring-context-4.0.xsd"><br />
<br />
<context:component-scan base-package="'''com.example.myapp'''" /><br />
<br />
<bean id="chunkTemplatesConfig" class="java.util.HashMap" scope="prototype"><br />
<constructor-arg><br />
<map key-type="java.lang.String" value-type="java.lang.String"><br />
<entry key="default_extension" value="chtml" /><br />
<entry key="cache_minutes" value="0" /><br />
<entry key="layers" value="" /><br />
<entry key="theme_path" value="" /><br />
<entry key="hide_errors" value="FALSE" /><br />
<entry key="error_log" value="" /><br />
<entry key="encoding" value="UTF-8" /><br />
<entry key="locale" value="" /><br />
<entry key="filters" value="" /><br />
</map><br />
</constructor-arg><br />
</bean><br />
<br />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><br />
<property name="viewClass" value="com.x5.template.spring.ChunkTemplateView"/><br />
<property name="prefix" value="/WEB-INF/themes/"/><br />
<property name="suffix" value=".chtml"/><br />
<property name="requestContextAttribute" value="rc"/><br />
</bean><br />
</beans><br />
<br />
4. Make a controller! The template name "helloworld" will resolve to /WEB-INF/themes/helloworld.chtml and any include/exec calls will be resolved relative to there.<br />
<br />
package com.example.myapp;<br />
<br />
import org.springframework.stereotype.Controller;<br />
import org.springframework.ui.Model;<br />
import org.springframework.web.bind.annotation.RequestMapping;<br />
import org.springframework.web.bind.annotation.RequestParam;<br />
import org.springframework.web.servlet.ModelAndView;<br />
<br />
@Controller<br />
public class HelloChunk<br />
{<br />
String message = "Welcome to Spring MVC!";<br />
<br />
@RequestMapping("/hello")<br />
public ModelAndView showMessage(<br />
@RequestParam(value = "name", required = false, defaultValue = "World") String name)<br />
{<br />
ModelAndView mv = new ModelAndView("helloworld");<br />
mv.addObject("message", message);<br />
mv.addObject("name", name);<br />
mv.addObject("page_name", "Spring 4 MVC - Hello Chunk");<br />
return mv;<br />
}<br />
<br />
@RequestMapping(value={"/chunk","/"})<br />
public ModelAndView chunkTime()<br />
{<br />
ModelAndView mv = new ModelAndView("chunkworld");<br />
mv.addObject("page_name", "Chunky Time");<br />
return mv;<br />
}<br />
}</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Spring_MVCSpring MVC2016-12-16T06:56:13Z<p>Tom McClure: /* Using bean getters in a template */</p>
<hr />
<div>== Can I use Chunk Templates in my Spring MVC project? ==<br />
<br />
Yes!<br />
<br />
Chunk 2.6.4 adds some new plumbing that makes it easy to use with [http://projects.spring.io/spring-framework/ Spring MVC], as a drop-in replacement template engine for Freemarker or Velocity.<br />
<br />
== Special features ==<br />
<br />
MVC Framework localized messages (defined in messages.properties etc) are available via a custom tag command:<br />
<br />
{% messages.msg.name %}<br />
<br />
Messages can be parameterized like so:<br />
<br />
{% messages.msg.name(`$a`,`$b`) %}<br />
<br />
A special request context tag is available, usually {$rc} but the name is configurable.<br />
<br />
The following request context values are available:<br />
<br />
{$rc.uri}<br />
{$rc.context_path}<br />
{$rc.servlet_path}<br />
{$rc.scheme}<br />
{$rc.method}<br />
{$rc.server_name}<br />
{$rc.remote_addr}<br />
{$rc.remote_host}<br />
{$rc.remote_user}<br />
<br />
== Using bean getters in a template ==<br />
<br />
When exposing beans (or POJO member variables) in a model, keep in mind that Chunk always converts camelCase to snake_case.<br />
<br />
So, <code>x.getFavoriteColor()</code> will be available in the template as <code>{% $x.favorite_color %}</code> -- and <code>x.isBigAndHairy()</code> must be checked like so:<br />
<br />
{% if $x.big_and_hairy %}...{% endif %}<br />
<br />
== Is there a sample project that I can clone? ==<br />
<br />
Why yes, just head here: [https://github.com/tomj74/chunk-spring-mvc Sample Spring MVC project on GitHub]<br />
<br />
You'll be up and running in no time.<br />
<br />
== Getting Started ==<br />
<br />
Here's what you will need:<br />
<br />
1. pom.xml dependencies:<br />
<br />
...<br />
<dependencies><br />
'''<dependency>'''<br />
'''<groupId>com.x5dev</groupId>'''<br />
'''<artifactId>chunk-springmvc</artifactId>'''<br />
'''<version>0.1.0</version>'''<br />
'''</dependency>'''<br />
'''<dependency>'''<br />
'''<groupId>com.x5dev</groupId>'''<br />
'''<artifactId>chunk-templates</artifactId>'''<br />
'''<version>3.2.4</version>'''<br />
'''</dependency>'''<br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-context</artifactId><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-aop</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-webmvc</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-web</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>jstl</groupId><br />
<artifactId>jstl</artifactId><br />
<version>1.2</version><br />
</dependency><br />
<dependency><br />
<groupId>commons-logging</groupId><br />
<artifactId>commons-logging</artifactId><br />
<version>1.2</version><br />
</dependency><br />
</dependencies><br />
<dependencyManagement><br />
<dependencies><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-context</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
</dependencies><br />
</dependencyManagement><br />
...<br />
<br />
2. WebContent/WEB-INF/web.xml<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<br />
<web-app version="2.4"<br />
xmlns="http://java.sun.com/xml/ns/j2ee"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee <br />
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" ><br />
<br />
<display-name>Chunky Spring</display-name><br />
<br />
<servlet><br />
<servlet-name>dispatcher</servlet-name><br />
<servlet-class><br />
org.springframework.web.servlet.DispatcherServlet<br />
</servlet-class><br />
<load-on-startup>1</load-on-startup><br />
</servlet><br />
<br />
<servlet-mapping><br />
<servlet-name>dispatcher</servlet-name><br />
<url-pattern>/</url-pattern><br />
</servlet-mapping><br />
<br />
<context-param><br />
<param-name>contextConfigLocation</param-name><br />
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value><br />
</context-param><br />
<br />
<listener><br />
<listener-class><br />
org.springframework.web.context.ContextLoaderListener<br />
</listener-class><br />
</listener><br />
<br />
</web-app><br />
<br />
3. WebContent/WEB-INF/dispatcher-servlet.xml - provide some Theme configuration and drop in a custom view class (source below) to viewResolver.<br />
<br />
Make sure to change <code>com.example.myapp</code> to the package where your annotated controllers live.<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<beans xmlns="http://www.springframework.org/schema/beans"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"<br />
xmlns:context="http://www.springframework.org/schema/context"<br />
xsi:schemaLocation="http://www.springframework.org/schema/beans<br />
<br />
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd<br />
http://www.springframework.org/schema/context <br />
http://www.springframework.org/schema/context/spring-context-4.0.xsd"><br />
<br />
<context:component-scan base-package="'''com.example.myapp'''" /><br />
<br />
<bean id="chunkTemplatesConfig" class="java.util.HashMap" scope="prototype"><br />
<constructor-arg><br />
<map key-type="java.lang.String" value-type="java.lang.String"><br />
<entry key="default_extension" value="chtml" /><br />
<entry key="cache_minutes" value="0" /><br />
<entry key="layers" value="" /><br />
<entry key="theme_path" value="" /><br />
<entry key="hide_errors" value="FALSE" /><br />
<entry key="error_log" value="" /><br />
<entry key="encoding" value="UTF-8" /><br />
<entry key="locale" value="" /><br />
<entry key="filters" value="" /><br />
</map><br />
</constructor-arg><br />
</bean><br />
<br />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><br />
<property name="viewClass" value="com.x5.template.spring.ChunkTemplateView"/><br />
<property name="prefix" value="/WEB-INF/themes/"/><br />
<property name="suffix" value=".chtml"/><br />
<property name="requestContextAttribute" value="rc"/><br />
</bean><br />
</beans><br />
<br />
4. Make a controller! The template name "helloworld" will resolve to /WEB-INF/themes/helloworld.chtml and any include/exec calls will be resolved relative to there.<br />
<br />
package com.example.myapp;<br />
<br />
import org.springframework.stereotype.Controller;<br />
import org.springframework.ui.Model;<br />
import org.springframework.web.bind.annotation.RequestMapping;<br />
import org.springframework.web.bind.annotation.RequestParam;<br />
import org.springframework.web.servlet.ModelAndView;<br />
<br />
@Controller<br />
public class HelloChunk<br />
{<br />
String message = "Welcome to Spring MVC!";<br />
<br />
@RequestMapping("/hello")<br />
public ModelAndView showMessage(<br />
@RequestParam(value = "name", required = false, defaultValue = "World") String name)<br />
{<br />
ModelAndView mv = new ModelAndView("helloworld");<br />
mv.addObject("message", message);<br />
mv.addObject("name", name);<br />
mv.addObject("page_name", "Spring 4 MVC - Hello Chunk");<br />
return mv;<br />
}<br />
<br />
@RequestMapping(value={"/chunk","/"})<br />
public ModelAndView chunkTime()<br />
{<br />
ModelAndView mv = new ModelAndView("chunkworld");<br />
mv.addObject("page_name", "Chunky Time");<br />
return mv;<br />
}<br />
}</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Spring_MVCSpring MVC2016-12-16T06:49:59Z<p>Tom McClure: /* How does it work? */</p>
<hr />
<div>== Can I use Chunk Templates in my Spring MVC project? ==<br />
<br />
Yes!<br />
<br />
Chunk 2.6.4 adds some new plumbing that makes it easy to use with [http://projects.spring.io/spring-framework/ Spring MVC], as a drop-in replacement template engine for Freemarker or Velocity.<br />
<br />
== Special features ==<br />
<br />
MVC Framework localized messages (defined in messages.properties etc) are available via a custom tag command:<br />
<br />
{% messages.msg.name %}<br />
<br />
Messages can be parameterized like so:<br />
<br />
{% messages.msg.name(`$a`,`$b`) %}<br />
<br />
A special request context tag is available, usually {$rc} but the name is configurable.<br />
<br />
The following request context values are available:<br />
<br />
{$rc.uri}<br />
{$rc.context_path}<br />
{$rc.servlet_path}<br />
{$rc.scheme}<br />
{$rc.method}<br />
{$rc.server_name}<br />
{$rc.remote_addr}<br />
{$rc.remote_host}<br />
{$rc.remote_user}<br />
<br />
== Using bean getters in a template ==<br />
<br />
When exposing beans (or POJO member variables) in a model, keep in mind that Chunk always converts camelCase to snake_case.<br />
<br />
So, x.getFavoriteColor() will be available in the template as {% $x.favorite_color %} -- and x.isBigAndHairy() must be checked like so:<br />
<br />
{% if $x.big_and_hairy %}...{% endif %}<br />
<br />
== Is there a sample project that I can clone? ==<br />
<br />
Why yes, just head here: [https://github.com/tomj74/chunk-spring-mvc Sample Spring MVC project on GitHub]<br />
<br />
You'll be up and running in no time.<br />
<br />
== Getting Started ==<br />
<br />
Here's what you will need:<br />
<br />
1. pom.xml dependencies:<br />
<br />
...<br />
<dependencies><br />
'''<dependency>'''<br />
'''<groupId>com.x5dev</groupId>'''<br />
'''<artifactId>chunk-springmvc</artifactId>'''<br />
'''<version>0.1.0</version>'''<br />
'''</dependency>'''<br />
'''<dependency>'''<br />
'''<groupId>com.x5dev</groupId>'''<br />
'''<artifactId>chunk-templates</artifactId>'''<br />
'''<version>3.2.4</version>'''<br />
'''</dependency>'''<br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-context</artifactId><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-aop</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-webmvc</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-web</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>jstl</groupId><br />
<artifactId>jstl</artifactId><br />
<version>1.2</version><br />
</dependency><br />
<dependency><br />
<groupId>commons-logging</groupId><br />
<artifactId>commons-logging</artifactId><br />
<version>1.2</version><br />
</dependency><br />
</dependencies><br />
<dependencyManagement><br />
<dependencies><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-context</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
</dependencies><br />
</dependencyManagement><br />
...<br />
<br />
2. WebContent/WEB-INF/web.xml<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<br />
<web-app version="2.4"<br />
xmlns="http://java.sun.com/xml/ns/j2ee"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee <br />
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" ><br />
<br />
<display-name>Chunky Spring</display-name><br />
<br />
<servlet><br />
<servlet-name>dispatcher</servlet-name><br />
<servlet-class><br />
org.springframework.web.servlet.DispatcherServlet<br />
</servlet-class><br />
<load-on-startup>1</load-on-startup><br />
</servlet><br />
<br />
<servlet-mapping><br />
<servlet-name>dispatcher</servlet-name><br />
<url-pattern>/</url-pattern><br />
</servlet-mapping><br />
<br />
<context-param><br />
<param-name>contextConfigLocation</param-name><br />
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value><br />
</context-param><br />
<br />
<listener><br />
<listener-class><br />
org.springframework.web.context.ContextLoaderListener<br />
</listener-class><br />
</listener><br />
<br />
</web-app><br />
<br />
3. WebContent/WEB-INF/dispatcher-servlet.xml - provide some Theme configuration and drop in a custom view class (source below) to viewResolver.<br />
<br />
Make sure to change <code>com.example.myapp</code> to the package where your annotated controllers live.<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<beans xmlns="http://www.springframework.org/schema/beans"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"<br />
xmlns:context="http://www.springframework.org/schema/context"<br />
xsi:schemaLocation="http://www.springframework.org/schema/beans<br />
<br />
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd<br />
http://www.springframework.org/schema/context <br />
http://www.springframework.org/schema/context/spring-context-4.0.xsd"><br />
<br />
<context:component-scan base-package="'''com.example.myapp'''" /><br />
<br />
<bean id="chunkTemplatesConfig" class="java.util.HashMap" scope="prototype"><br />
<constructor-arg><br />
<map key-type="java.lang.String" value-type="java.lang.String"><br />
<entry key="default_extension" value="chtml" /><br />
<entry key="cache_minutes" value="0" /><br />
<entry key="layers" value="" /><br />
<entry key="theme_path" value="" /><br />
<entry key="hide_errors" value="FALSE" /><br />
<entry key="error_log" value="" /><br />
<entry key="encoding" value="UTF-8" /><br />
<entry key="locale" value="" /><br />
<entry key="filters" value="" /><br />
</map><br />
</constructor-arg><br />
</bean><br />
<br />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><br />
<property name="viewClass" value="com.x5.template.spring.ChunkTemplateView"/><br />
<property name="prefix" value="/WEB-INF/themes/"/><br />
<property name="suffix" value=".chtml"/><br />
<property name="requestContextAttribute" value="rc"/><br />
</bean><br />
</beans><br />
<br />
4. Make a controller! The template name "helloworld" will resolve to /WEB-INF/themes/helloworld.chtml and any include/exec calls will be resolved relative to there.<br />
<br />
package com.example.myapp;<br />
<br />
import org.springframework.stereotype.Controller;<br />
import org.springframework.ui.Model;<br />
import org.springframework.web.bind.annotation.RequestMapping;<br />
import org.springframework.web.bind.annotation.RequestParam;<br />
import org.springframework.web.servlet.ModelAndView;<br />
<br />
@Controller<br />
public class HelloChunk<br />
{<br />
String message = "Welcome to Spring MVC!";<br />
<br />
@RequestMapping("/hello")<br />
public ModelAndView showMessage(<br />
@RequestParam(value = "name", required = false, defaultValue = "World") String name)<br />
{<br />
ModelAndView mv = new ModelAndView("helloworld");<br />
mv.addObject("message", message);<br />
mv.addObject("name", name);<br />
mv.addObject("page_name", "Spring 4 MVC - Hello Chunk");<br />
return mv;<br />
}<br />
<br />
@RequestMapping(value={"/chunk","/"})<br />
public ModelAndView chunkTime()<br />
{<br />
ModelAndView mv = new ModelAndView("chunkworld");<br />
mv.addObject("page_name", "Chunky Time");<br />
return mv;<br />
}<br />
}</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Spring_MVCSpring MVC2016-12-16T06:49:13Z<p>Tom McClure: /* Can I use Chunk Templates in my Spring MVC project? */</p>
<hr />
<div>== Can I use Chunk Templates in my Spring MVC project? ==<br />
<br />
Yes!<br />
<br />
Chunk 2.6.4 adds some new plumbing that makes it easy to use with [http://projects.spring.io/spring-framework/ Spring MVC], as a drop-in replacement template engine for Freemarker or Velocity.<br />
<br />
== Special features ==<br />
<br />
MVC Framework localized messages (defined in messages.properties etc) are available via a custom tag command:<br />
<br />
{% messages.msg.name %}<br />
<br />
Messages can be parameterized like so:<br />
<br />
{% messages.msg.name(`$a`,`$b`) %}<br />
<br />
A special request context tag is available, usually {$rc} but the name is configurable.<br />
<br />
The following request context values are available:<br />
<br />
{$rc.uri}<br />
{$rc.context_path}<br />
{$rc.servlet_path}<br />
{$rc.scheme}<br />
{$rc.method}<br />
{$rc.server_name}<br />
{$rc.remote_addr}<br />
{$rc.remote_host}<br />
{$rc.remote_user}<br />
<br />
== Using bean getters in a template ==<br />
<br />
When exposing beans (or POJO member variables) in a model, keep in mind that Chunk always converts camelCase to snake_case.<br />
<br />
So, x.getFavoriteColor() will be available in the template as {% $x.favorite_color %} -- and x.isBigAndHairy() must be checked like so:<br />
<br />
{% if $x.big_and_hairy %}...{% endif %}<br />
<br />
== Is there a sample project that I can clone? ==<br />
<br />
Why yes, just head here: [https://github.com/tomj74/chunk-spring-mvc Sample Spring MVC project on GitHub]<br />
<br />
You'll be up and running in no time.<br />
<br />
== How does it work? ==<br />
<br />
Here's what you will need:<br />
<br />
1. pom.xml dependencies:<br />
<br />
...<br />
<dependencies><br />
'''<dependency>'''<br />
'''<groupId>com.x5dev</groupId>'''<br />
'''<artifactId>chunk-springmvc</artifactId>'''<br />
'''<version>0.1.0</version>'''<br />
'''</dependency>'''<br />
'''<dependency>'''<br />
'''<groupId>com.x5dev</groupId>'''<br />
'''<artifactId>chunk-templates</artifactId>'''<br />
'''<version>3.2.4</version>'''<br />
'''</dependency>'''<br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-context</artifactId><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-aop</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-webmvc</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-web</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>jstl</groupId><br />
<artifactId>jstl</artifactId><br />
<version>1.2</version><br />
</dependency><br />
<dependency><br />
<groupId>commons-logging</groupId><br />
<artifactId>commons-logging</artifactId><br />
<version>1.2</version><br />
</dependency><br />
</dependencies><br />
<dependencyManagement><br />
<dependencies><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-context</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
</dependencies><br />
</dependencyManagement><br />
...<br />
<br />
2. WebContent/WEB-INF/web.xml<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<br />
<web-app version="2.4"<br />
xmlns="http://java.sun.com/xml/ns/j2ee"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee <br />
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" ><br />
<br />
<display-name>Chunky Spring</display-name><br />
<br />
<servlet><br />
<servlet-name>dispatcher</servlet-name><br />
<servlet-class><br />
org.springframework.web.servlet.DispatcherServlet<br />
</servlet-class><br />
<load-on-startup>1</load-on-startup><br />
</servlet><br />
<br />
<servlet-mapping><br />
<servlet-name>dispatcher</servlet-name><br />
<url-pattern>/</url-pattern><br />
</servlet-mapping><br />
<br />
<context-param><br />
<param-name>contextConfigLocation</param-name><br />
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value><br />
</context-param><br />
<br />
<listener><br />
<listener-class><br />
org.springframework.web.context.ContextLoaderListener<br />
</listener-class><br />
</listener><br />
<br />
</web-app><br />
<br />
3. WebContent/WEB-INF/dispatcher-servlet.xml - provide some Theme configuration and drop in a custom view class (source below) to viewResolver.<br />
<br />
Make sure to change <code>com.example.myapp</code> to the package where your annotated controllers live.<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<beans xmlns="http://www.springframework.org/schema/beans"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"<br />
xmlns:context="http://www.springframework.org/schema/context"<br />
xsi:schemaLocation="http://www.springframework.org/schema/beans<br />
<br />
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd<br />
http://www.springframework.org/schema/context <br />
http://www.springframework.org/schema/context/spring-context-4.0.xsd"><br />
<br />
<context:component-scan base-package="'''com.example.myapp'''" /><br />
<br />
<bean id="chunkTemplatesConfig" class="java.util.HashMap" scope="prototype"><br />
<constructor-arg><br />
<map key-type="java.lang.String" value-type="java.lang.String"><br />
<entry key="default_extension" value="chtml" /><br />
<entry key="cache_minutes" value="0" /><br />
<entry key="layers" value="" /><br />
<entry key="theme_path" value="" /><br />
<entry key="hide_errors" value="FALSE" /><br />
<entry key="error_log" value="" /><br />
<entry key="encoding" value="UTF-8" /><br />
<entry key="locale" value="" /><br />
<entry key="filters" value="" /><br />
</map><br />
</constructor-arg><br />
</bean><br />
<br />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><br />
<property name="viewClass" value="com.x5.template.spring.ChunkTemplateView"/><br />
<property name="prefix" value="/WEB-INF/themes/"/><br />
<property name="suffix" value=".chtml"/><br />
<property name="requestContextAttribute" value="rc"/><br />
</bean><br />
</beans><br />
<br />
4. Make a controller! The template name "helloworld" will resolve to /WEB-INF/themes/helloworld.chtml and any include/exec calls will be resolved relative to there.<br />
<br />
package com.example.myapp;<br />
<br />
import org.springframework.stereotype.Controller;<br />
import org.springframework.ui.Model;<br />
import org.springframework.web.bind.annotation.RequestMapping;<br />
import org.springframework.web.bind.annotation.RequestParam;<br />
import org.springframework.web.servlet.ModelAndView;<br />
<br />
@Controller<br />
public class HelloChunk<br />
{<br />
String message = "Welcome to Spring MVC!";<br />
<br />
@RequestMapping("/hello")<br />
public ModelAndView showMessage(<br />
@RequestParam(value = "name", required = false, defaultValue = "World") String name)<br />
{<br />
ModelAndView mv = new ModelAndView("helloworld");<br />
mv.addObject("message", message);<br />
mv.addObject("name", name);<br />
mv.addObject("page_name", "Spring 4 MVC - Hello Chunk");<br />
return mv;<br />
}<br />
<br />
@RequestMapping(value={"/chunk","/"})<br />
public ModelAndView chunkTime()<br />
{<br />
ModelAndView mv = new ModelAndView("chunkworld");<br />
mv.addObject("page_name", "Chunky Time");<br />
return mv;<br />
}<br />
}</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Spring_MVCSpring MVC2016-12-16T06:40:19Z<p>Tom McClure: /* How does it work? */</p>
<hr />
<div>== Can I use Chunk Templates in my Spring MVC project? ==<br />
<br />
Yes!<br />
<br />
Chunk 2.6.4 adds some new plumbing that makes it easy to use with [http://projects.spring.io/spring-framework/ Spring MVC], as a drop-in replacement template engine for Freemarker or Velocity.<br />
<br />
== Is there a sample project that I can clone? ==<br />
<br />
Why yes, just head here: [https://github.com/tomj74/chunk-spring-mvc Sample Spring MVC project on GitHub]<br />
<br />
You'll be up and running in no time.<br />
<br />
== How does it work? ==<br />
<br />
Here's what you will need:<br />
<br />
1. pom.xml dependencies:<br />
<br />
...<br />
<dependencies><br />
'''<dependency>'''<br />
'''<groupId>com.x5dev</groupId>'''<br />
'''<artifactId>chunk-springmvc</artifactId>'''<br />
'''<version>0.1.0</version>'''<br />
'''</dependency>'''<br />
'''<dependency>'''<br />
'''<groupId>com.x5dev</groupId>'''<br />
'''<artifactId>chunk-templates</artifactId>'''<br />
'''<version>3.2.4</version>'''<br />
'''</dependency>'''<br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-context</artifactId><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-aop</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-webmvc</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-web</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
<dependency><br />
<groupId>jstl</groupId><br />
<artifactId>jstl</artifactId><br />
<version>1.2</version><br />
</dependency><br />
<dependency><br />
<groupId>commons-logging</groupId><br />
<artifactId>commons-logging</artifactId><br />
<version>1.2</version><br />
</dependency><br />
</dependencies><br />
<dependencyManagement><br />
<dependencies><br />
<dependency><br />
<groupId>org.springframework</groupId><br />
<artifactId>spring-context</artifactId><br />
<version>4.1.5.RELEASE</version><br />
</dependency><br />
</dependencies><br />
</dependencyManagement><br />
...<br />
<br />
2. WebContent/WEB-INF/web.xml<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<br />
<web-app version="2.4"<br />
xmlns="http://java.sun.com/xml/ns/j2ee"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br />
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee <br />
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" ><br />
<br />
<display-name>Chunky Spring</display-name><br />
<br />
<servlet><br />
<servlet-name>dispatcher</servlet-name><br />
<servlet-class><br />
org.springframework.web.servlet.DispatcherServlet<br />
</servlet-class><br />
<load-on-startup>1</load-on-startup><br />
</servlet><br />
<br />
<servlet-mapping><br />
<servlet-name>dispatcher</servlet-name><br />
<url-pattern>/</url-pattern><br />
</servlet-mapping><br />
<br />
<context-param><br />
<param-name>contextConfigLocation</param-name><br />
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value><br />
</context-param><br />
<br />
<listener><br />
<listener-class><br />
org.springframework.web.context.ContextLoaderListener<br />
</listener-class><br />
</listener><br />
<br />
</web-app><br />
<br />
3. WebContent/WEB-INF/dispatcher-servlet.xml - provide some Theme configuration and drop in a custom view class (source below) to viewResolver.<br />
<br />
Make sure to change <code>com.example.myapp</code> to the package where your annotated controllers live.<br />
<br />
<?xml version="1.0" encoding="UTF-8"?><br />
<beans xmlns="http://www.springframework.org/schema/beans"<br />
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"<br />
xmlns:context="http://www.springframework.org/schema/context"<br />
xsi:schemaLocation="http://www.springframework.org/schema/beans<br />
<br />
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd<br />
http://www.springframework.org/schema/context <br />
http://www.springframework.org/schema/context/spring-context-4.0.xsd"><br />
<br />
<context:component-scan base-package="'''com.example.myapp'''" /><br />
<br />
<bean id="chunkTemplatesConfig" class="java.util.HashMap" scope="prototype"><br />
<constructor-arg><br />
<map key-type="java.lang.String" value-type="java.lang.String"><br />
<entry key="default_extension" value="chtml" /><br />
<entry key="cache_minutes" value="0" /><br />
<entry key="layers" value="" /><br />
<entry key="theme_path" value="" /><br />
<entry key="hide_errors" value="FALSE" /><br />
<entry key="error_log" value="" /><br />
<entry key="encoding" value="UTF-8" /><br />
<entry key="locale" value="" /><br />
<entry key="filters" value="" /><br />
</map><br />
</constructor-arg><br />
</bean><br />
<br />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><br />
<property name="viewClass" value="com.x5.template.spring.ChunkTemplateView"/><br />
<property name="prefix" value="/WEB-INF/themes/"/><br />
<property name="suffix" value=".chtml"/><br />
<property name="requestContextAttribute" value="rc"/><br />
</bean><br />
</beans><br />
<br />
4. Make a controller! The template name "helloworld" will resolve to /WEB-INF/themes/helloworld.chtml and any include/exec calls will be resolved relative to there.<br />
<br />
package com.example.myapp;<br />
<br />
import org.springframework.stereotype.Controller;<br />
import org.springframework.ui.Model;<br />
import org.springframework.web.bind.annotation.RequestMapping;<br />
import org.springframework.web.bind.annotation.RequestParam;<br />
import org.springframework.web.servlet.ModelAndView;<br />
<br />
@Controller<br />
public class HelloChunk<br />
{<br />
String message = "Welcome to Spring MVC!";<br />
<br />
@RequestMapping("/hello")<br />
public ModelAndView showMessage(<br />
@RequestParam(value = "name", required = false, defaultValue = "World") String name)<br />
{<br />
ModelAndView mv = new ModelAndView("helloworld");<br />
mv.addObject("message", message);<br />
mv.addObject("name", name);<br />
mv.addObject("page_name", "Spring 4 MVC - Hello Chunk");<br />
return mv;<br />
}<br />
<br />
@RequestMapping(value={"/chunk","/"})<br />
public ModelAndView chunkTime()<br />
{<br />
ModelAndView mv = new ModelAndView("chunkworld");<br />
mv.addObject("page_name", "Chunky Time");<br />
return mv;<br />
}<br />
}</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-11-12T14:12:10Z<p>Tom McClure: </p>
<hr />
<div>Welcome to the Chunk Templates Wiki.<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.4</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-07-05T02:31:31Z<p>Tom McClure: </p>
<hr />
<div>Welcome to the Chunk Templates Wiki.<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.3</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.3</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-06-23T14:52:48Z<p>Tom McClure: /* Where should I put my template files? */</p>
<hr />
<div>Welcome to the Chunk Templates Wiki.<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.2</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.2</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath, themeLayers);<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-06-12T16:47:30Z<p>Tom McClure: </p>
<hr />
<div>Welcome to the Chunk Templates Wiki.<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.2</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.2</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath,themeLayers);<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-06-05T18:47:48Z<p>Tom McClure: </p>
<hr />
<div>Welcome to the Chunk Templates Wiki.<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.1</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.1</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath,themeLayers);<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-02-20T16:56:08Z<p>Tom McClure: /* Chunk Style Guide */</p>
<hr />
<div>Welcome to the Chunk Templates Wiki.<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.0</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath,themeLayers);<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|urlencode %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-02-20T16:45:33Z<p>Tom McClure: /* Chunk Style Guide */</p>
<hr />
<div>Welcome to the Chunk Templates Wiki.<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.0</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath,themeLayers);<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|s/ /%20/g %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-02-20T16:45:15Z<p>Tom McClure: /* Chunk Style Guide */</p>
<hr />
<div>Welcome to the Chunk Templates Wiki.<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.0</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath,themeLayers);<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)}<br />
{% $price|sprintf($%,.2f) %}<br />
<br />
{$url|s/ /%20/g}<br />
{% $url|s/ /%20/g %}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-02-20T03:10:33Z<p>Tom McClure: /* Undefined tag values, Null-trapping */</p>
<hr />
<div>Welcome to the Chunk Templates Wiki.<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.0</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath,themeLayers);<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)} or {% $price|sprintf($%,.2f) %}<br />
{$url|s/ /%20/g}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag, value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag", value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag", value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-02-19T16:53:10Z<p>Tom McClure: /* Chunk Style Guide */</p>
<hr />
<div>Welcome to the Chunk Templates Wiki.<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.0</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath,themeLayers);<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)} or {% $price|sprintf($%,.2f) %}<br />
{$url|s/ /%20/g}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag,value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag",value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag",value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClurehttps://www.x5software.com/chunk/wiki/index.php?title=Chunk_DocumentationChunk Documentation2016-02-19T15:53:59Z<p>Tom McClure: /* Chunk Includes with {% include [...] %} */</p>
<hr />
<div>Welcome to the Chunk Templates Wiki.<br />
<br />
Visit the [https://github.com/tomj74/chunk-templates Chunk Templates Project Home] on Github or<br />
[https://github.com/tomj74/chunk-templates/releases/latest download the latest release].<br />
<br />
The Chunk Templates library is now available as an artifact on Maven Central. Add to your maven pom.xml:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.0</version><br />
</dependency><br />
<br />
Chunk is a Template Engine for Java, similar to Apache Velocity or FreeMarker. Chunk has been field-tested in many applications.<br />
<br />
Features:<br />
<br />
* Compatible with Android and GAE (Google App Engine) and [[Spring MVC]].<br />
* Nestable loops and conditionals (if-elseIf-else).<br />
* Includes and Macros for easy composition.<br />
* Curly-brace {$tags} pop nicely in a backdrop full of &lt;AngleBrackets&gt;.<br />
* Flexible null-handling; template designer may specify default tag values.<br />
* Swiss army knife of powerful in-tag [[Chunk_Tag_Filters|filters]], including regex (regular expressions), sprintf.<br />
* Localization framework.<br />
* Rapid MVC: Glue a "model" object (or objects) to a "view" template with a single line of controller code.<br />
* Define multiple snippets per template file.<br />
* Support for [[#Chunk_Theme_Layers:_Inheritance|theme layers and inheritance]].<br />
* Highly optimized codebase.<br />
* Hooks for extending.<br />
* [[Eclipse Template Editor plugin]] available with syntax highlighting &amp; more.<br />
<br />
<br />
= THE "CHUNK" TEMPLATING SYSTEM =<br />
<br />
== Introduction ==<br />
<br />
Philosophy: presentation code (HTML) and source code (Java) are like<br />
oil and water. They shouldn't mix, and when they do, it's not pretty.<br />
<br />
cf php, jsp, asp, ... or java servlets with giant HTML strings hardcoded into the source. Blegh.<br />
<br />
Fortunately, keeping layout and business logic apart is a breeze with a good templating system. Chunk aims to be just that.<br />
<br />
------------------------<br />
Q: Does the Java community really need yet another templating engine?<br />
<br />
A: Popular solutions like Freemarker and Velocity are targeted at software engineers, but require them to learn totally new (and in my opinion ill-chosen) syntax for doing the same things they do in their base development languages. Chunk offers a much simpler, easy-to-grasp syntax and an intentionally simple, stateless featureset. Chunk templates have a laser-focused job to do: format data that's been "blessed" for presentation. The "View" layer of your app should not be triggering application code or making method calls on application objects. This division of responsibility keeps your presentation templates simple enough that designers can create and edit them independently. Simple template contracts encourage application developers to keep business logic in the Java code, and to pass clean static/model data off to the template engine for presentation.<br />
<br />
------------------------<br />
Q: Does it cost anything to use?<br />
<br />
A: Chunk will always be 100% free software.<br />
<br />
------------------------<br />
Q: Where can I get my hands on it?<br />
<br />
A: [https://github.com/tomj74/chunk-templates/releases/latest Download Latest Release of Chunk] from GitHub. Tinker with a clone from [https://github.com/tomj74/chunk-templates GitHub].<br />
Or, just add it to your Maven build:<br />
<dependency><br />
<groupId>com.x5dev</groupId><br />
<artifactId>chunk-templates</artifactId><br />
<version>3.2.0</version><br />
</dependency><br />
<br />
------------------------<br />
Q: I hate stuffy documentation. I learn by example. Is there a recipes page?<br />
<br />
A: Start here: [http://www.x5software.com/chunk/examples/ChunkExample Chunk Recipes] - served using Chunk, of course!<br />
<br />
------------------------<br />
Q: Requirements - What version of Java do I need? Any dependencies?<br />
<br />
A: Chunk works best on Java 1.5 and up. Some features require [http://code.google.com/p/json-smart/ json-smart] or [http://sourceforge.net/projects/jeplite/ jeplite] but most functionality just requires the main [https://sourceforge.net/projects/chunk-templates/files/ chunk templates jar].<br />
<br />
------------------------<br />
Q: How do I use Chunk with Spring MVC?<br />
<br />
A: See the [[Spring MVC]] integration guide.<br />
<br />
------------------------<br />
Q: Who are you?<br />
<br />
A: X5 Software is Tom McClure, [http://www.fishysudoku.com/ Sudoku shark], web developer, [http://www.myspace.com/TommySasso musician] and dad. My day job is maintaining part of the stack behind [http://crunchyroll.com/ Crunchyroll]. Read my [http://www.dagblastit.com/ blog at dagblastit.com].<br />
------------------------<br />
<br />
<br />
=== Getting Started: Creating HTML with Chunk ===<br />
<br />
Here's a quick-start guide (or skip straight to the [http://www.x5software.com/chunk/examples/ChunkExample Examples]):<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/chunk/wiki/#Getting_Started:_Creating_HTML_with_Chunk Standard Chunk Tag Syntax]<br />
<br />
<span class="noflip"></span><span style="color:blue">{$...}</span> <span style="color:slateblue">{.cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
<span class="noflip"></span>Chunk 1.7 and below used ~ and ^ for {~tags} and {^commands} - this syntax may become deprecated but for now the library is back-compatible. This documentation is available '''[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk in the old tag syntax].'''<br />
<br />
<span class="noflip"></span>[http://www.x5software.com/cgi-bin/ChunkDocumentation#Getting_Started:_Creating_HTML_with_Chunk Old Tag Syntax]<br />
<br />
<span style="color:blue">{~...}</span> <span style="color:slateblue">{^cmd} {+...}</span> <span style="color:red">{!-- ... --}</span> <span style="color:green">{#...}</span>[define template here]<span style="color:green">{#}</span><br />
tag command include comment snippet<br />
<br />
==== Whitespace in braces: {% expr %} ====<br />
<br />
Until 2.5, chunk tag syntax was very compact, and whitespace inside braces was either not allowed or highly restricted. This is fine for a simple {$tag} but can be hard on the eyes for flow control, commands, or tags with complex filter chains.<br />
<br />
So, Chunk 2.5 introduced a new whitespace-friendly {% tag_expr %} syntax, as popularized by django, jinja, twig, etc.<br />
* '''Expressions are assumed to be commands''' (eg, flow control directives) so it is not necessary to prefix if, loop, exec, etc. with a period, but $tag variables still need the leading dollar sign.<br />
<br />
{% if ($x) %}<br />
{% $tag|default(Hello) %}<br />
{% endif %}<br />
<br />
* The old {/if} style for ending blocks just looked wrong in the new style, so writing out {% endif %} is now preferred.<br />
* Tags now have a little breathing room when they need it, and template flow is way easier on the eyes.<br />
<br />
==== Example ====<br />
<br />
Let's take a quick look at what it's like to code with Chunk templates:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- The snippets defined in this template are used on the welcome page --}</span><br />
<br />
<span style="color:red">{!-- template for main content on welcome page --}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
&lt;div&gt;<br />
Hello <span style="color:blue">{$name}</span>! Welcome to the site!<br />
&lt;/div&gt;<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $x %}</span><br />
<br />
Widget Name: <span style="color:blue">{$x.name}</span>&lt;br/&gt;<br />
Widget SKU: <span style="color:blue">{$x.sku}</span>&lt;br/&gt;<br />
<br />
In Stock: <span style="color:slateblue">{% if ($x.in_stock) %}</span>Yes<span style="color:slateblue">{% else %}</span>No<span style="color:slateblue">{% endif %}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($x.related_widgets) %}</span><br />
&lt;br/&gt;<br />
<span style="color:slateblue">{% loop in $x.related_widgets as $related %}</span><br />
Related: &lt;a href="<span style="color:blue">{$related.detail_url}</span>"&gt;<span style="color:blue">{$related.name}</span>&lt;/a&gt;&lt;br/&gt;<br />
<span style="color:slateblue">{% endloop %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:slateblue">{% divider %}</span><br />
&lt;hr/&gt;<br />
<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!-- template for footer on welcome page --}</span><br />
<span style="color:green">{#footer}</span><br />
&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved.<br />
<span style="color:green">{#}</span><br />
<br />
--------------------------------------<br />
<br />
Tags are denoted by <code>{$tag_name}</code> -- tags function as placeholders for<br />
dynamic values provided at runtime.<br />
<br />
Multiple snippets of html (or xml, or whatever) may be defined in a single file.<br />
<br />
For example, the hello.chtml file above defines three templates:<br />
hello<br />
hello#welcome<br />
hello#footer<br />
<br />
The root "hello" template (just whitespace in this example) consists of all<br />
text in the file, minus the <code><span style="color:green">{#subtemplate}</span>...<span style="color:green">{#}</span></code> snippet definitions.<br />
<br />
<code><span style="color:red">{!-- comments --}</span></code> are also stripped out and will never appear in any output.<br />
<br />
------------------------- java<br />
// Example java code:<br />
import com.x5.template.Theme;<br />
import com.x5.template.Chunk;<br />
<br />
...<br />
<br />
Theme theme = new Theme(); // a standard theme with no layers.<br />
Chunk c = theme.makeChunk("hello#welcome"); // from src/themes/hello.chtml<br />
<br />
c.set("name", getName() );<br />
c.set("site_name", "Widgets International");<br />
<br />
buf.append( c.toString() ); // outputs "Hello Bob! Welcome to the site!"<br />
<br />
// Need to use a template over and over?<br />
// Let's re-use the chunk that we already set up.<br />
//<br />
c.resetTags();<br />
buf.append( c.toString() ); // outputs "Hello {$name}! Welcome to the site!"<br />
<br />
// To grab a raw snippet and skip doing any tag processing,<br />
// use theme.fetch( ... )<br />
//<br />
buf.append( theme.fetch("hello#footer") );<br />
// outputs "&amp;copy; 2011 Dewey Cheatham and Howe. All Rights Reserved."<br />
<br />
...<br />
<br />
// Want to render directly to a stream? Easy peasy (new in 1.6).<br />
Theme theme = new Theme();<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
<br />
c.set("this", "that");<br />
c.set("tag", "value");<br />
<br />
Writer out = getStreamWriter();<br />
c.render( out );<br />
--------------------------------------<br />
<br />
=== Where should I put my template files? ===<br />
<br />
Your templates should be in a folder named "themes" that lives somewhere on the classpath for your app. Template files can be on the filesystem or even in a jar. Tip: If you put them in {{nowrap|<code>src/themes/*.chtml</code>}} then Eclipse will automatically copy the templates into the build folder. You can include template files in your jar build just as you would with any image resources (or jar the templates separately) and at runtime Chunk can fish the templates out of any jar in the classpath. During development, you can maintain the themes folder somewhere else if you prefer, as long as (A) your build routine copies the "themes" folder into your build or (B) your runtime classpath includes the "themes" folder's direct parent folder.<br />
<br />
Advanced: Servlets are often deployed to appservers where each application has a special classloader and its own classpath. This classpath can be difficult for Chunk (or any 3rd-party library) to see. To address this disconnect, your servlet can give an explicit hint to the Theme object to share some visibility into the app's resource context. Here's how:<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
Theme theme = new Theme();<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
What if I want to load templates from a different folder?<br />
<br />
public void doGet(HttpServletRequest req, HttpServletResponse resp)<br />
{<br />
String templatePath = "/path/to/my/templates";<br />
String themeLayers = "";<br />
<br />
Theme theme = new Theme(templatePath,themeLayers);<br />
theme.setJarContext(getServletContext());<br />
...<br />
}<br />
<br />
== Chunk Style Guide ==<br />
<br />
Template filenames should be named in lowercase with no periods before<br />
the extension and no spaces anywhere. Use underscores in place of<br />
spaces where needed. You can use any extension but .chtml and .cxml<br />
are preferred for templates that contain html and xml.<br />
<br />
Example template filenames:<br />
<br />
welcome.chtml<br />
message.cxml<br />
account_settings.chtml<br />
<br />
Tags should follow the same guidelines. Mixed case is allowed but<br />
strongly discouraged, and the only legal punctuation is dashes and<br />
underscores with underscores strongly preferred over dashes. Spaces<br />
are not legal inside a tag name. Numbers are ok.<br />
<br />
Example tags:<br />
<br />
{$name}<br />
{$home_phone}<br />
{$menu_bar}<br />
{$left_nav}<br />
{$header_1}<br />
<br />
{$results:Nothing found!}<br />
{$address_line_2:}<br />
<br />
{$price|sprintf($%,.2f)} or {% $price|sprintf($%,.2f) %}<br />
{$url|s/ /%20/g}<br />
<br />
{+other_template}<br />
{+template_ref#fully_qualified}<br />
{+#subtemplate}<br />
<br />
{% include template_ref#snippet_xyz %}<br />
{% includeIf($secure==yes).#snippet_abc %}<br />
{% alt_repository.template_name %}<br />
<br />
{.if ($cond) } A {.else} B {/if}<br />
<br />
...or...<br />
<br />
{% if ($cond) %}<br />
A<br />
{% else %}<br />
B<br />
{% endif %}<br />
<br />
=== Leave out the extension! ===<br />
<br />
Template references usually omit the filename extension. For<br />
example, a snippet reference like <code>hello.chtml#welcome</code><br />
is usually redundant since .chtml is already the default extension for most<br />
themes. <code>hello#welcome</code> is the recommended form.<br />
<br />
However, if your theme contains files with multiple different extensions,<br />
you can fully qualify the reference (when the extension does not match<br />
the theme's default extension), eg: <code>hello.txt</code> or<br />
<code>hello.txt#welcome</code>.<br />
<br />
When a template can't be found, the engine will insert some helpful error text<br />
into the output (or to stderr if so configured) with a complete report on where<br />
it looked for the template on the filesystem/classpath.<br />
<br />
== Chunk Tag :Defaults ==<br />
<br />
A tag can provide its own default value. Here's the syntax:<br />
<br />
{$tag:DEFAULT VALUE}<br />
<br />
This is equivalent to:<br />
<br />
<span style="color:slateblue">{% if ($tag) %}</span><span style="color:blue">{$tag}</span><span style="color:slateblue">{% else %}</span>DEFAULT VALUE<span style="color:slateblue">{% endif %}</span><br />
<br />
But be careful! In both cases, the else clause only fires if the {$tag} really is undefined. The empty string "" is '''not''' interpreted as null/undefined.<br />
<br />
If you want a tag to disappear by default, you must supply a blank default value.<br />
The tag will look like this:<br />
<br />
{$error_msg:}<br />
<br />
Or if you're working with HTML, you could use a non-rendering comment:<br />
<br />
{$error_msg:&lt;!-- no errors --&gt;}<br />
<br />
{{blockquote|<br />
'''Advanced Usage Note - Nested Braces'''<br />
<br />
Defaults may contain nested tags. Nesting braces,<br />
however, is not allowed, and will result in a parsing error.<br />
In fact, unescaped braces are not allowed anywhere inside a tag,<br />
because the final inside brace will be mistaken for the final outer<br />
brace by the speedy parser.*<br />
<br />
For example, you could opt to show the username when no data is<br />
available for the full name:<br />
<br />
Example 1:<br />
Correct: {$full_name:$username} THIS IS OK<br />
INCORRECT: {$full_name:{$username}<span></span>} <-- nested braces, WILL NOT WORK<br />
<br />
Example 2:<br />
BAD {$name:{.include.#default_name}<span></span>} THIS WILL NOT WORK<br />
GOOD {$name:.include.#default_name} THIS IS OK<br />
<br />
As you can see, you can even use the special .include.template_ref syntax<br />
(like in the intro_paragraph tag below) to designate a template snippet<br />
to appear in the tag space by default. Read the [[#Chunk_Includes_with_.7B.25_include_.5B....5D_.25.7D|INCLUDES]]<br />
section to learn more.<br />
<br />
* There is one exception: curly braces are legal inside a regular expression filter. So, this regex filter that truncates the tag value after the first 10 characters is OK:<br />
<br />
{$long_value&#124;s/^(.{10}).*$/$1/}<br />
}}<br />
<br />
=== Tag Preservation (no value? leave tag alone) ===<br />
<br />
Standard behavior when a tag value is null or never defined is for the<br />
tag to pass through to the final output unchanged. Tag preservation<br />
is by design, since the output can then be used as a template or tag<br />
value for re-processing in a "parent" context, ie later on where the<br />
presentation code finally has access to an appropriate value for the<br />
tag.<br />
<br />
During development, tag preservation makes it easy to examine<br />
the final output of the template engine and see exactly which tags<br />
you still need to provide values for in the code.<br />
<br />
=== A recursive template engine ===<br />
<br />
Nest your chunks, use tags in your tag values. Chunk will unravel it all.<br />
When you call Chunk's <code>.toString()</code> or <code>.render(out)</code> methods,<br />
a tag resolution algorithm recursively scans all expanded tag values for additional tags and<br />
attempts to resolve any that it finds. A recursion depth limit<br />
prevents the template engine from getting stuck in infinite recursion loops.<br />
<br />
=== Undefined tag values, Null-trapping ===<br />
<br />
Notice how the {$friends:} tag below has an empty default value and<br />
disappears in the final output, but the next tag {$goobers} provides no<br />
default. Because of Chunk's tag-preservation feature, the {$goobers}<br />
tag simply passes through into the final<br />
output. This strategy could be leveraged to send the output through<br />
the processor again later on, when a good value for goobers might<br />
become available. If that's not your plan, it's generally better to<br />
supply a default for all your tags (even if it's something like "ERROR!<br />
Name Undefined!").<br />
<br />
------------------------- hello.chtml<br />
Hello <span style="color:blue">{$name:there}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>! <span style="color:blue">{$friends:}</span> <span style="color:blue">{$goobers}</span><br />
<br />
&lt;p&gt;<span style="color:blue">{$intro_paragraph:.include.#default_intro}</span>&lt;/p&gt;<br />
<br />
<span style="color:green">{#default_intro}</span><br />
Here at Widgets of America, we specialize in widgets. Nobody knows<br />
widgets better than we do, and you can bet the widget farm that you<br />
can't stump our &lt;a href="<span style="color:blue">{$webroot}</span>/experts"&gt;Widget Experts&lt;/a&gt;.<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
// Normally there would be a bunch of c.set("tag_name", tagValue) calls here,<br />
// but for this example we want to illustrate null tag behavior.<br />
//<br />
// A chunk with no rules will make all tags pass through or render to their default values<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
{{blockquote|<br />
'''Important usage note: Null-trapping and set() v. setOrDelete()'''<br />
<br />
Chunk .set(tag,value) calls turn null values into the empty string "" by default. This null-trapping is usually helpful, since you avoid seeing "null" spelled out in your template output, or worse, risking that a template won't execute due to a NullPointerException.<br />
<br />
Here's how it works: The standard method for creating tag replacement rules -<br />
<br />
Chunk.set("tag",value);<br />
<br />
interprets null values as the empty string!<br />
<br />
If you really want null values to result in the tag being undefined, you must use<br />
<br />
Chunk.setOrDelete("tag",value);<br />
<br />
If you don't want to have to care about this distinction, just use the onempty() filter in your templates instead of the colon syntax.<br />
<br />
Hello {$name&#124;onempty(there)}! ...<br />
<br />
}}<br />
<br />
Careful, because of null-trapping (see usage note above) this variation is ''not'' the same:<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBecomeEmptyString = null;<br />
<br />
c.set("site_name", willBecomeEmptyString);<br />
c.set("name", willBecomeEmptyString);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello ! Welcome to ! {$goobers} ..."<br />
<br />
To make the template defaults fire (i.e. to make the tag truly undefined), you have to use setOrDelete(x,y) instead of set(x,y). Or, call unset("tag") to delete a tag directly.<br />
------------------------- java<br />
// ...<br />
Chunk c = theme.makeChunk("hello");<br />
<br />
String willBeUndefined = null;<br />
<br />
c.setOrDelete("site_name", willBeUndefined);<br />
c.setOrDelete("name", willBeUndefined);<br />
<br />
return c.toString();<br />
<br />
------------------------- output<br />
"Hello there! Welcome to the site! {$goobers} ..."<br />
<br />
== Chunk Tag Filters: Powerful <code>{$tag|transformations}</code> ==<br />
<br />
A helpful library of [[Chunk_Tag_Filters|built-in text filters]] are available via the pipe (|) character.<br />
<br />
Filters provide a way to alter/transform the tag value presentation<br />
on-the-fly, directly in the template.<br />
<br />
You may contribute your own filters. [[#Contribute a filter|See below]] for how.<br />
<br />
The following [[Chunk_Tag_Filters|built-in filters]] (follow the link for complete docs) are currently provided with the Chunk library:<br />
<br />
{$any_tag|qs} escapes double and single quotes with a backslash (don't "flub" -> don\'t \"flub\")<br />
{$any_tag|uc} will transform the text to all uppercase<br />
{$any_tag|lc} all lowercase<br />
{$any_tag|md5} md5 hash (hex, or try md5base64)<br />
{$any_tag|sha1} sha-1 hash (hex, or try shabase64)<br />
{$any_tag|base64} base64-encode<br />
{$any_tag|base64decode} decode base64-encoded string<br />
{$any_tag|url} url-encode (make+safe+for+query+strings). Alias: |urlencode<br />
{$any_tag|urldecode} url decode (eg: my%20string -> my string)<br />
{$any_tag|html} escapes html (uses &amp;amp; &amp;lt; &amp;gt; &amp;quot; and &amp;apos;)<br />
{$any_tag|xml} same as |html<br />
{$any_tag|unescape} (1.8) unescape xml-escaped entities, including eg &amp;#123; and &amp;#x03BB;<br />
{$any_tag|trim} removes leading and trailing whitespace<br />
{$any_tag|defang} removes HTML tag markers etc. to foil xss script-injection attacks<br />
{$any_tag|translate} translate via the localization engine. Aliases: |xlate,|_<br />
{$any_tag|type} outputs STRING LIST OBJECT NULL CHUNK depending on the tag value type<br />
<br />
<span style="color:red">{!-- dynamic/parameterized filters: ie these can take function-style arguments --}</span><br />
<span style="font-weight:bold">{$any_tag|sprintf(%05.3f)} applies sprintf formatting</span><br />
{$any_tag|filter(#x_template)} (2.0.1) executes #x_template, binding {$x} to the value of {$any_tag}<br />
{$any_tag|selected(abc)} outputs selected="selected" if $any_tag == abc<br />
{$any_tag|checked(abc)} outputs checked="checked" if $any_tag == abc<br />
{$any_tag|indent(3)} indents multi-line values by x spaces<br />
{$any_tag|alternate(even_output,odd_output)} new in 1.7 - if tag value is numeric and even...<br />
{$any_tag|qcalc(+20)} perform a single math operation on a numeric tag value<br />
{$any_tag|calc("*(y+z)",$y,$z)} perform a complex math operation (requires [http://sourceforge.net/projects/jeplite/ jeplite])<br />
{$any_tag|default(oops)} (2.5) when tag is null/undefined, output "oops" (same as {$any_tag:oops})<br />
{$any_tag|onempty(oops)} (1.8) normal tag-resolution - but when tag is empty or undefined, output "oops"<br />
{$any_tag|ondefined(output)} see below<br />
{$any_tag|onmatch(/RE1/,output1,/RE2/,output2)} see below<br />
<br />
<span style="color:red">{!-- special filters for handling string arrays/lists --}</span><br />
{$any_tag|join(, )} output a string array with a delimiter between each value<br />
{$any_tag|get(0)} (1.8) access the 0th (ie first) element in the list/array<br />
<br />
<span style="color:red">{!-- for regex fans, this one is especially cool: arbitrary regex-style search and replace! --}</span><br />
<span style="font-weight:bold">{$any_tag|s/[0-9]/#/g} perl-style search+replace with regular expressions</span><br />
<br />
These pipe modifiers may be chained. Multiple filters will be applied in<br />
the order specified:<br />
{$any_tag|trim|lc|md5|uc}<br />
<br />
In the example above, the tag value will have leading and trailing whitespace removed first, then the string will be converted to lowercase, the lowercase string will get MD5-hashed, and finally, the hex hash output (lowercase by default) will be converted to uppercase.<br />
<br />
<br />
=== Mixing Tag |Filters and Tag :Defaults ===<br />
<br />
The pipe modifier may be mixed with the colon modifier. The colon<br />
value may be placed before or after the filters (but not inside a<br />
filter chain). Use the colon first to have the filter transform<br />
applied to it, and last to skip the filter transform.<br />
<br />
ie:<br />
{$any_tag:don't|qs} and {$any_tag|qs:don't} are not equivalent.<br />
<br />
When $any_tag is null:<br />
{$any_tag:don't|qs} => don\'t<br />
{$any_tag|qs:don't} => don't<br />
<br />
The filter is applied in both cases if any_tag is non-null, but the<br />
default value is not filtered in the second case.<br />
<br />
=== Use sparingly: <code>|ondefined(...)</code> and <code>|onmatch(...)</code> ===<br />
<br />
Filters <code>|onmatch(...)</code> and <code>|ondefined(...)</code> open the door for ultra-compact (but hard to read) xslt-style transform logic. These can be very powerful but remember, '''don't code your entire app's logic in tag filter transforms!''' Use of these filters should be reserved for presentation/layout logic only. Extensive use of these filters will result in contorted, hard-to-follow templates.<br />
<br />
If, after careful consideration, you decide that you really need branching logic to sit in your presentation layer templates, <strong>you probably want to use the more-readable <code>{.if}</code> construct instead</strong> (documented under "[[#Advanced Usage|advanced]]" section below).<br />
<br />
A. ondefined usage<br />
<br />
ondefined(output)<br />
* if tag value is null or zero-length: display tag :default if provided, otherwise display nothing.<br />
* if tag value is non-null and non-empty, display the specified output.<br />
<br />
{$any_tag|ondefined(some text)}<br />
{$any_tag|ondefined($some_tag)}<br />
{$any_tag|ondefined(+some_template)}<br />
{$any_tag|ondefined(.some_external.content)}<br />
<br />
<br />
B. onmatch usage<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])<br />
onmatch(/RE/,output[,/RE2/,output2[,...]])nomatch(def_output)<br />
<br />
* if tag value is null or does not match any RE: display nothing (or def_output)<br />
* otherwise, display the output that is paired with the first matching regexp.<br />
<br />
------------- Example 1<br />
{$any_tag|onmatch(/a/,ABC)}<br />
<br />
null -> ""<br />
fox -> ""<br />
a -> "ABC"<br />
cat -> "ABC" (for exact matches, use /^a$/ syntax)<br />
<br />
-------------- Example 2: switch/case style<br />
{$any_tag|onmatch(/ca/,Cat,/a/,ABC)nomatch(DEF)}<br />
<br />
null -> "DEF"<br />
fox -> "DEF"<br />
a -> "ABC"<br />
car -> "Cat"<br />
bar -> "ABC"<br />
<br />
Note the optional nomatch clause. For example:<br />
<br />
--------------- Example 3: not just static strings<br />
<br />
The onmatch and ondefined output arguments allow tag references and template snippet "includes" to be used anywhere that you would place static output text.<br />
<br />
{$any_tag|onmatch(/1/,+template_one,/2/,+template_two)nomatch($errmsg)}<br />
<br />
1 -> include template "template_one"<br />
2 -> include template "template_two"<br />
3/null/etc. -> display the value of the {$errmsg} tag<br />
<br />
---------------<br />
Future ideas:<br />
{$any_tag|usd} formats numbers as U.S. currency eg 30000 -> $30,000.00<br />
for now, just use {$amount|sprintf($%,.2f)}<br />
<br />
{$any_tag|num(00.00)} applies DecimalFormat formatting<br />
{$any_tag|date(yyyy-MM-dd)} applies SimpleDateFormat formatting<br />
<br />
== Chunk _[Localization] ==<br />
<br />
Any string in your template may be marked for translation by wrapping it in the _[Localization] marker syntax.<br />
<br />
Create a file in the classpath named <code>locale/xx_XX/translate.csv</code> and Chunk will find it and use it for translation when appropriate.<br />
<br />
You can set the locale of a single Chunk or set a default locale for your whole Theme object.<br />
<br />
------------------------- java<br />
<br />
Theme theme = new Theme();<br />
theme.setLocale("fr_FR");<br />
Chunk c = theme.makeChunk();<br />
c.append("_[That's it!] _[Localization].");<br />
out.println( c.toString() );<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Here are my welcome page strings<br />
"That's it!","Et voila!"<br />
"Localization","Localisation"<br />
<br />
Et voila! Localisation.<br />
<br />
=== Advanced usage, filter tag ===<br />
<br />
You can add curly braces and use %s format string markers for more advanced examples:<br />
<br />
{_[Account #%s balance is %s],$account,$balance}<br />
<br />
------------------------- locale/fr_FR/translate.csv<br />
<br />
# Bank balance page strings<br />
"Account #%s balance is %s","Compte Nº %s solde est %s"<br />
<br />
And you can send the output of any tag through the translator with the new "_" filter, like so:<br />
<br />
{$tag|_}<br />
<br />
or more verbosely using the filter aliases "xlate" or "translate" like so:<br />
<br />
{$tag|xlate} {$tag|translate}<br />
<br />
== Chunk MACROS with {% exec %} ==<br />
<br />
Macros calls are a special way of including another template snippet. With an {.exec} block, template A<br />
can include template B ''and'' assign values to template B's tags all from within template A, relieving java of some of the busy work.<br />
<br />
Let's say you have some presentation element that is repeated over and over in<br />
your project. Maybe it's a button or a box with a fancy border, or a three-column<br />
page layout that's used on a lot of pages in your project.<br />
<br />
For example, I have a CSS sliding-doors button template with some fairly ugly HTML that I use over and over, just changing the label or adding a css class when I need to alter the default button. With macros, I don't have to copy and paste that button HTML over and over (and I don't have to look at it and remember what an awful hack it is every time I need a pretty button).<br />
<br />
<span style="color:slateblue">{% exec BUTTON %}</span><br />
<span style="color:purple">{$btn_label = Add to Cart}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Normally the java-template contract leaves it to your java code to assign values/rules for<br />
tag replacement. With a macro, your template can take on a contract with another template<br />
and assume some of the responsibility for setting those tag values.<br />
<br />
Appropriate macro use eliminates a lot of java code. I don't have to set up a button chunk in my java code and prep the label value there, and in fact this obscure presentation detail vanishes from the app logic, as it should.<br />
<br />
Macros can make your templates easier to read too - instead of squinting at a dense block of HTML, you're looking at a clean macro call.<br />
<br />
Macro syntax:<br />
<br />
<span style="color:slateblue">{% exec TEMPLATE_SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
The end parameter markers "{=}" are optional.<br />
<br />
Simple tag values may be provided using the syntax for $param3 and $param4<br />
above, but the value must not contain special characters (including braces<br />
and the = and # signs) and must not include any template directives or tags.<br />
<br />
In the example above, the assignment syntax used for $param1 and $param2 is much more flexible.<br />
Whatever you sandwich between the start-def <code>{$...=}</code><br />
and end-def <code>{=}</code> tags (or everything up to the next start-def tag) is the value of that tag in the macro call.<br />
<br />
Multiline values are okay, and chunk tags may be used or even a nested macro exec call.<br />
<br />
However, longer values make the macro call harder to read. If your values<br />
are especially long, move the value out into a snippet and pull it in with<br />
<code>{% include #snippet %}</code>.<br />
<br />
Style note: I strongly recommend naming template files and #snippets in ALL_UPPERCASE if<br />
they are to be used as macros.<br />
<br />
=== Other macro exec parameter formats: JSON, XML ===<br />
<br />
Starting in Chunk 1.7, macro invocation parameters can now be specified as a JSON object or even XML, ie in addition to the default/original parameter format. JSON is especially handy for providing data to your macro template since it has rich support for encoding simple strings, lists, and objects in infinite nesting combinations.<br />
<br />
Notes: Chunk does not include a json parser. If you do want to specify args in json or json-strict format, you will need to add the [http://code.google.com/p/json-smart/ json-smart lib] to the classpath (a free download). Chunk does include a rudimentary, non-validating XML parser. XML element "attributes" (like &lt;child gender="boy"&gt;) are available to the template by appending an "@" accessor to the tag (eg, {$child@gender} ).<br />
<br />
<span style="color:red">{!-- standard param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME %}</span><br />
<span style="color:purple">{$param1=}</span>value1<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param2=}</span>value2<span style="color:purple">{=}</span><br />
<span style="color:purple">{$param3 = Simple Value}</span><br />
<span style="color:purple">{$param4 = 400}</span><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new json param format.<br />
-- Note 1: Parameters can have more exciting/useful names than just param1,param2.<br />
-- Note 2: This is technically not valid JSON. Use "@json-strict" if you care about such things.<br />
--}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @json %}</span><br />
{ param1 : 'value1',<br />
param2 : '{$some_tag}',<br />
param3 : 'Simple Value',<br />
param4 : 400,<br />
param5 : ['an','array','of','strings'],<br />
param6 : {some:'object',with:'its own params'},<br />
param7 : [{name:'array of objects'},{name:'etc'}],<br />
}<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
<span style="color:red">{!-- new xml param format example. --}</span><br />
<span style="color:slateblue">{% exec TEMPLATE#SNIPPET_NAME @xml %}</span><br />
<values><br />
<param1>value1</param1><br />
<param2>{$some_tag}</param2><br />
<param3><![CDATA[some <html>]]></param3><br />
<param4>400</param4><br />
</values><br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Since 1.7 exec supports an inline template, either by using the special {% body %}...{% endbody %} markers or (since 2.6.3) by encasing the data in {% data %}...{% enddata %} tags (or both).<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<br />
<span style="color:slateblue">{% data %}</span><br />
<span style="color:purple">{$x = 2}</span><br />
<span style="color:purple">{$y = Ringo}</span><br />
<span style="color:slateblue">{% enddata %}</span><br />
<br />
<span style="color:slateblue">{% body %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endbody %}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
Implicit inline body (no template ref in the exec call, data encased):<br />
<br />
<span style="color:slateblue">{% exec %}</span><br />
<span style="color:slateblue">{% data @json %}</span><br />
{ x: 2, y: 'Ringo' }<br />
<span style="color:slateblue">{% enddata %}</span><br />
The number <span style="color:blue">{$x}</span> Beatle was <span style="color:blue">{$y}</span>.<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
=== Examples ===<br />
<br />
Example macro invocation:<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span> <span style="color:red">{!-- beginning of macro call --}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span> <span style="color:red">{!-- end of macro call --}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- BOX.chtml<br />
<br />
<span style="color:red">{!------------------------------------------------<br />
-- I hate writing HTML table code, so I just<br />
-- use this template over and over as a macro.<br />
-- I could even reimplement this layout with divs and css<br />
-- and not have to touch the 27 places like above in hello.chtml<br />
-- where it gets called.<br />
------------------------------------------------}</span><br />
<br />
&lt;table border="1" width="<span style="color:blue">{$box_width:300}</span>"&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_title}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<span style="color:blue">{$box_content}</span>&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
------------------------- java<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
return c.toString();<br />
<br />
------------------------- output<br />
<br />
&lt;table border="1" width="300"&gt;<br />
&lt;tr&gt;&lt;td&gt;Welcome to the site!&lt;/td&gt;&lt;/tr&gt;<br />
&lt;tr&gt;&lt;td&gt;<br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
&lt;/td&gt;&lt;/tr&gt;<br />
&lt;/table&gt;<br />
<br />
This idea will appear to be outside the box.<br />
<br />
== Chunk Includes with <code>{% include [...] %}</code> ==<br />
<br />
You don't have to use macros to provide simpler directives from the<br />
template. Want to just "include" another template? It's easy!<br />
<br />
The recommended syntax for includes is:<br />
{% include template_name %}<br />
<br />
To include a subtemplate snippet:<br />
{% include template_name#snippet_name %}<br />
<br />
When the snippet is defined in the same file, you may omit the template filename:<br />
{% include #snippet_name %}<br />
<br />
Include expects a static reference. If the template reference is in the value of a tag, use backticks:<br />
{% include `$template_name` %}<br />
<br />
<br />
=== Conditional Includes with <code>{% includeIf(...).[...] %}</code> ===<br />
<br />
A conditional include a la <code>{% includeIf([cond]).[template] %}</code> was added in 1.1. The syntax is compact but it's a bit hard to read for the uninitiated, so I recommend using the <code>{% if %}</code> construct with a distinct <code>{% include %}</code> tag instead. As a bonus for going that route, you get an "else" clause which <code>{% includeIf %}</code> does not support.<br />
<br />
For example:<br />
<br />
<span style="color:slateblue">{% if ($potatoes == "yes") %}</span><br />
<span style="color:slateblue">{% include #potatoes %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:slateblue">{% include #spam %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
But if you like cryptic/compact, go for it. On template expansion, if the [cond] expression is true, the [template] is included.<br />
<br />
Unlike standard tags, when the condition is not met, the entire tag<br />
disappears from the output.<br />
<br />
Style note: Try to avoid coding your entire app with chains of <code>{% includeIf(...)... %}</code> tags.<br />
<br />
<br />
Example includeIf(...) uses:<br />
<br />
{!-- Only these three simple tests are supported --}<br />
<br />
{!-- 1. if $tag exists, if $tag doesn't exist; ie, null test --}<br />
<span style="color:slateblue">{% includeIf($username).hello#hello_username %}</span><br />
<span style="color:slateblue">{% includeIf(!$username).hello#hello_anon %}</span><br />
<br />
{!-- 2. if equals, if doesn't equal string/tag-var --}<br />
<span style="color:slateblue">{% includeIf($username == Bob).hello#hello_bob %}</span><br />
<span style="color:slateblue">{% includeIf($username != Bob).hello#hello_anon %}</span><br />
<span style="color:slateblue">{% includeIf($username == $def_username).hello#login %}</span><br />
<br />
{!-- 3. if matches regex, if doesn't match regex --}<br />
<span style="color:slateblue">{% includeIf($username =~ /(jane|john)/i).hello#hello_janejohn %}</span><br />
<span style="color:slateblue">{% includeIf($username !~ /(jane|john)/i).hello#hello_anon %}</span><br />
<br />
Note that these examples are roughly equivalent to the more efficient<br />
and more powerful (but even less readable) "ondefined" and "onmatch" filters:<br />
<span style="color:blue">{$username|ondefined(+tpl_a):+tpl_b}</span><br />
<span style="color:blue">{$username|onmatch(/regex/,+tpl_a)nomatch(+tpl_b)}</span><br />
<br />
<br />
The "+" is shorthand for .include. [[#Chunk_Tag_Filters:_Powerful_.7B.24tag.7Ctransformations.7D|More about filters]].<br />
<br />
Shorthand include/includeIf syntax:<br />
<span style="color:blue">{+my_content}</span> is the same as <span style="color:slateblue">{% include my_content %}</span><br />
<span style="color:blue">{+(cond)my_content}</span> is the same as <span style="color:slateblue">{% includeIf(cond).my_content %}</span><br />
<br />
...but this shorthand should be used sparingly. I prefer to spell out<br />
the word "include" to promote readability.<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!-----------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
------------------------- top_nav.chtml<br />
<br />
<span style="color:red">{!-- put the sitewide navigation bar here --}</span><br />
...<br />
<br />
== Combining Chunk Macros with Includes ==<br />
<br />
You can combine macro and include syntax. Multi-line macro parameters never look great, so this technique can greatly<br />
increase the readability of your layout templates by tucking away that messy parameter into its own snippet:<br />
<br />
------------------------- hello.chtml<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#welcome}</span><br />
<br />
<span style="color:slateblue">{% exec BOX %}</span><br />
<br />
<span style="color:purple">{$box_title=}</span>Welcome to <span style="color:blue">{$site_name:the site}</span>!<span style="color:purple">{=}</span><br />
<span style="color:purple">{$box_content=}</span> <span style="color:slateblue">{% include hello#box_text %}</span> <span style="color:purple">{=}</span><br />
<br />
<span style="color:slateblue">{% endexec %}</span><br />
<br />
This idea will appear to be outside the box.<br />
<br />
<span style="color:green">{#}</span><br />
<br />
<br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:red">{!-- This marks a new, separate snippet definition. --}</span><br />
<span style="color:red">{!-- This snippet is included from the snippet above. --}</span><br />
<span style="color:red">{!--------------------------------------------------------}</span><br />
<span style="color:green">{#box_text}</span><br />
Help!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
I'm trapped in a box!<br />
<span style="color:green">{#}</span><br />
<br />
Note: in the template above, <span style="color:slateblue">{% include hello#box_text %}</span> could be written as just <span style="color:slateblue">{% include #box_text %}</span> and the result would be the same. You can leave out the filename part of the snippet reference if the target snippet is defined in the same file.<br />
<br />
== Nesting Snippet Definitions (You can, but don't) ==<br />
<br />
Snippet definitions may be nested, but this practice is strongly<br />
discouraged since it can lead to confusion.<br />
<br />
For the headstrong, here's an example reference to a nested snippet<br />
definition:<br />
<br />
{% include filename#snippet#nested_snippet %}<br />
<br />
Which was defined like so:<br />
<br />
...<br />
<span style="color:green">{#snippet}</span><br />
bla bla bla<br />
<span style="color:green">{#nested_snippet}</span>foo foo <span style="color:slateblue">{% include #important_stuff %}</span> foo<span style="color:green">{#}</span><br />
bla bla bla<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- put the important stuff down here --}</span><br />
<span style="color:green">{#important_stuff}</span><br />
...<br />
<span style="color:green">{#}</span><br />
<br />
In particular, this can be misleading because shorthand snippet<br />
references are always expanded with just the filename -- so the<br />
above include is a reference to filename#important_stuff and not<br />
to either of the following snippets, like you might expect:<br />
<br />
filename#snippet#important_stuff<br />
filename#snippet#nested_snippet#important_stuff<br />
<br />
== Advanced Usage ==<br />
<br />
=== Special Directives: {% tagStack %} {% if %} {% loop %} ===<br />
<br />
The core library provides a few useful internal special tags.<br />
<br />
==== tagStack ====<br />
<br />
The .tagStack tag shows all available tagnames with data<br />
in the current chunk template expansion context. Super-useful for debugging.<br />
<br />
<span style="color:slateblue">{% tagStack %}</span> or try <span style="color:slateblue">{% tagStack(html) %}</span><br />
<br />
----<br />
<br />
==== if ====<br />
<br />
Chunk 1.3 introduces a full-featured nestable {% if %}...{% endif %} construct.<br />
<br />
<span style="color:slateblue">{% if ($weather) %}</span><br />
<span style="color:slateblue">{% if ($weather == sunny) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_sun %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == rainy) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_rain %}</span><br />
<span style="color:slateblue">{% elseIf ($weather =~ /snowy|flurries/) %}</span><br />
<span style="color:darkslategrey">{% include #picture_of_snow %}</span><br />
<span style="color:slateblue">{% elseIf ($weather == $weather_in_philly) %}</span><br />
<span style="color:darkslategrey">{% include #always_sunny %}</span><br />
<span style="color:slateblue">{% else %}</span><br />
<span style="color:darkslategrey">{% include #default_picture %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
Note: Chunk's conditional expressions are quite limited. The lightweight parser can only handle the simple string value tests listed below. Sorry, '''conditions may not include greater-than or less-than tests''' (eg &gt; &lt; &lt;= gt lt etc) but the [http://www.x5software.com/chunk/wiki/Chunk_Tag_Filters#comp.28.3Coperation.3E.29 comp] filter might fit your needs (handles numeric comparison with a constant).<br />
<br />
Chunk's conditional expressions '''do not support the && and || (and/or) operators.''' However, the inclusion of regular expression pattern matching should cover most of your exotic comparison needs. Anything more involved is best handled in your business logic anyway, and not in your presentation layer.<br />
<br />
The following tests will function as expected:<br />
<br />
<span style="color:red">{!-- existence --}</span><br />
<span style="color:slateblue">{% if ($tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if (!tag) %}</span>...<span style="color:slateblue">{% endif %}</span> or <span style="color:slateblue">{% if (!$tag) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- equality / inequality --}</span><br />
<span style="color:slateblue">{% if ($tag == apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != apples) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag == "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag != "apples") %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a == $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag_a != $tag_b) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- pattern matching --}</span><br />
<span style="color:slateblue">{% if ($tag =~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<span style="color:slateblue">{% if ($tag !~ /apples|oranges/) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
Filter modifiers are legal and may be applied to any if-condition tags.<br />
<br />
<span style="color:red">{!-- this will execute even if tag_a is PICKLES and tag_b is pIcKLeS --}</span><br />
<span style="color:slateblue">{% if ($tag_a|lc == $tag_b|lc) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
<span style="color:red">{!-- this will execute when the tag is (a) undefined, (b) empty or (c) actually set to the string "EMPTY"<br />
-- the onempty(''output'') filter is new in 1.8 --}</span><br />
<span style="color:slateblue">{% if ($tag|onempty(EMPTY) == EMPTY) %}</span>...<span style="color:slateblue">{% endif %}</span><br />
<br />
{{blockquote|<br />
'''Philosophical note'''<br />
<br />
Purists and dogmatic templaters may insist that {% loop %} and {% if %} tags have no place in a proper template engine. For a long time Chunk resisted anything resembling flow control within the templates themselves, but related snippets often had to be located very far from one another and readability suffered. So, rejoice! There is a happy medium.<br />
<br />
Chunk still strives to leave business logic over on the java side of the fence (and therefore keeps visibility into java objects at a bare minimum). But dagblastit, sometimes you're presenting some content and you need to branch and loop. Have at it. But, you've been warned: don't code your whole app in your templates!<br />
}}<br />
<br />
{{blockquote|<br />
'''Compact syntax'''<br />
<br />
For one-liners, sometimes the compactness of the pre-2.5 syntax can come in handy:<br />
{.if ($tag) }...{.else}...{/if}<br />
<br />
This compact syntax is used internally and will be supported indefinitely.<br />
<br />
However, it can be confusing to mix compact style tags with the new easier-to-read (but less compact) expression tag syntax.<br />
<br />
For this reason, the longer {% expr %} syntax is now the official preferred style in all our documentation and examples.<br />
<br />
}}<br />
----<br />
<br />
==== loop ====<br />
<br />
The {% loop %} tag is also very versatile.<br />
<br />
<span style="color:grey">{!-- templates defined in-place between loop tag and matching endloop tag. --}</span><br />
<span style="color:slateblue">{% loop in $data_var as $x array_index_tags="true" %}</span><br />
...<span style="color:blue">{$x[0]} {$x[1]}</span>...<br />
...<span style="color:blue">{$x.fruit} {$x.quantity}</span>...<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
no data!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
Note: The two lines in the loop block above demonstrate array-index access vs column-name access. The outputs are equivalent (see table definition below).<br />
<br />
[[fruit,quantity],<br />
[apples,4],<br />
[bananas,3],<br />
[canteloupe,7]]<br />
<br />
.loop options:<br />
* divider="<dividertext>"<br />
* counter="<$tagname>[,offset[,step]]"<br />
* counter_tags="true|false" - provide counter tags {$0} (zero-indexed loop counter) and {$1} (loop counter starting at 1) automatically.<br />
* array_index_tags="true|false" - access table row data by column index, eg {$x[2]}<br />
* first_last="true|false|<$firsttag,$lasttag,$placetag>" - provide {$place} tag (evaluates to "first" and/or "last") and {$first} and {$last} tags on matching iterations.<br />
<br />
These options are specified after the "in $list as $x" clause like so (quoting the parameter values is optional):<br />
<br />
{% loop in $list as $x counter=$i,1 array_index_tags=true first_last=$primero,$ultimo %}<br />
<br />
{{blockquote|<br />
'''Looping Tips'''<br />
<br />
* A divider, if desired, is usually easier to specify after the loop body using a {% divider %}...{% enddivider %} section. The closing tag is optional.<br />
<br />
* An {% onEmpty %}...{% endonEmpty %} section should almost always be included, to output in case there is nothing to iterate over. The closing tag is optional.<br />
<br />
* Loop over a subset by adding a <code>&#124;slice(from:to)</code> filter to the $list var.<br />
<br />
* Loop in reverse order by adding a <code>&#124;reverse</code> filter to the $list var.<br />
<br />
See [[http://www.x5software.com/chunk/examples/ChunkExample?example=loop&loc=en_US looping examples]] in the recipes area.<br />
}}<br />
<br />
The $data_var can be a tag of type LIST or whose value is an inline table (uses a combination of JSON and<br />
CSV styles; make sure to escape in-string commas and square brackets<br />
with backslash) defined in a string like so:<br />
<br />
Other types that can be looped over with {% loop %}:<br />
* String array (treated as a one-column headerless table).<br />
* an array of objects that implement Map.<br />
* an array of POJOs/beans.<br />
* anything that implements the <code>com.x5.util.TableData</code> interface.<br />
* a single Map/POJO/Bean (use " as $key:value" to iterate over the object's properties).<br />
<br />
For example, I use a QueryResultTable class that runs an arbitrary SELECT query. QueryResultTable groks the column labels and gathers up all the results in an array of String arrays (so, I only use it for relatively small result sets). It implements TableData so I can stash the results directly into the Chunk and let the template decide how to present the data.<br />
<br />
String SQL = "SELECT ...";<br />
QueryResultTable table = QueryResultTable.doSelect(SQL);<br />
<br />
Chunk c = theme.makeChunk("pretty_table");<br />
c.set("results", table);<br />
<br />
buf.append( c.toString() );<br />
<br />
----<br />
<br />
=== Chunk Theme Layers: Inheritance ===<br />
<br />
The engine also supports overriding a base theme with multiple "theme" layers. Let's say you are making a new "skin" for your app but you want to retain most of the existing template work.<br />
<br />
Put your base theme templates in themes/base and any new "override" templates in themes/fancy.<br />
<br />
themes/<br />
\-- base/<br />
\-- main_shell.chtml<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
\-- fancy/<br />
\-- hello.chtml<br />
\-- BUTTON.chtml<br />
<br />
Then, in the "fancy" files, just define the snippets that you actually want to revamp. When initializing your theme, list the layers the way you plan to stack them up (in order from low precedence to high precedence).<br />
<br />
Theme fancy = new Theme("base,fancy");<br />
<br />
==== Inherit root template with {% super %} ====<br />
<br />
Be careful! If you override a template and just include a few override snippets, you will wipe out the parent root template (ie, with the empty version in your override layer). All {.include.file#snippet} references will function as intended, but a theme reference to the bare {.include.file} will return your override layer's blank version of that file's root template.<br />
<br />
There are a couple ways around this problem. (A) You could copy your root template content from the other layer -- but this is a bad idea since the two copies will get out of sync. Instead, try (B) place the special <code>{% super %}</code> tag at the top of your document like so:<br />
<br />
fancy/hello.chtml<br />
<br />
<span style="color:red">{!-- prettier hello --}</span><br />
<span style="color:blue">{% super %}</span><br />
<br />
<span style="color:green">{#some_snippet}</span><br />
&lt;div class="purty"&gt;I'm so pretty now!&lt;/div&gt;<br />
<span style="color:green">{#}</span><br />
<br />
This will signal to the template engine to defer to the layer underneath for the root template. You do not need to do this for named snippets, only for the root content! If you're happy with the snippet as defined in the layer below, just leave the snippet out of the higher layer completely.<br />
<br />
=== Rapid MVC ===<br />
<br />
==== Simple Binding with POJO/Bean ====<br />
<br />
Since 2.1, Chunk no longer requires boxing your objects in a DataCapsule. Just bind the object to a tag prefix with a regular "set" call. At render time, Chunk will take a snapshot of the POJO's public attributes (or the Bean's public getters/accessors) and treat them like any other Map object. Even nested references will work!<br />
<br />
Item item = new Item("Best Cat Videos", 9.99, "DVD");<br />
item.recommend = new Item("Best Cat Videos II", 8.99, "DVD");<br />
c.set("item", item);<br />
<br />
&lt;div class="item"&gt;<br />
$<span style="color:blue">{$item.price|sprintf(%0.2f)}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.title}</span>&lt;br/&gt;<br />
<span style="color:blue">{$item.type}</span>&lt;br/&gt;<br />
<br />
<span style="color:slateblue">{% if ($item.recommend) %}</span><br />
&lt;div class="recommendation"&gt;<br />
You may also like: <span style="color:blue">{$item.recommend.title}</span><br />
&lt;/div&gt;<br />
<span style="color:slateblue">{% endif %}</span><br />
<br />
&lt;/div&gt;<br />
<br />
The {% loop in $list as $x %} tag can loop over arrays/lists of "plain old" objects as well.<br />
<br />
* CamelCase fieldnames are converted to lowercase_with_underscores.<br />
* Primitives (int, long, double, float, etc. including boxed primitives Integer Long etc.) are converted to Strings.<br />
* Boolean values are the string "TRUE" if true, or null/undefined when false, to facilitate {% if ($x.is_something) %} branching.<br />
* Arrays and Lists can be looped over with {% loop in $obj.some_list as $x %}<br />
<br />
Other types of fields (besides String) are treated as nested POJOs,<br />
so you can reference a child object's properties from the template<br />
as you would expect: {$obj.child.child_property}<br />
<br />
An object's methods (aside from "getter" accessor methods) are *not* callable from the template, in keeping with the read-only stateless nature of Chunk templates.<br />
<br />
==== Encapsulation with DataCapsule ====<br />
<br />
'''NB: Boxing objects with DataCapsule is no longer recommended. DataCapsule boxing is still supported, but since 2.1, binding objects to your templates is far easier using the POJO/Bean methods documented above.'''<br />
<br />
Any object (a Model-style object) that implements com.x5.util.DataCapsule can be glued right into a template (your "view") with a single line of controller code. When you implement DataCapsule, use getExports() to provide a list of public methods that exist in your model class, with the following call signature: String getXXX() -- ie, simple accessor methods that take no arguments and return a String.<br />
<br />
public class Widget implements DataCapsule<br />
{<br />
...<br />
<br />
public String[] getExports()<br />
{<br />
return new String[]{<br />
"getName",<br />
"getID",<br />
"getPrice price_usd" // change tag name to widget_price_usd<br />
};<br />
}<br />
<br />
public String getExportPrefix()<br />
{<br />
return "widget";<br />
}<br />
}<br />
<br />
In your controller...<br />
<br />
...<br />
Widget widget = getWidget();<br />
<br />
Chunk c = theme.makeChunk("widget_detail");<br />
c.addData(widget); // all exported Widget methods are now available tags in the template<br />
<br />
// what if you need two widgets on the same page?<br />
// No problem, provide the optional second arg and the tags will get an object-style prefix.<br />
String objName = "x";<br />
c.addData(getOtherWidget(), objName);<br />
...<br />
<br />
widget_detail.chtml:<br />
<br />
Name: <span style="color:blue">{$widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$widget_price_usd}</span>&lt;br/&gt;<br />
<br />
Related widget:<br />
Name: <span style="color:blue">{$x.widget_name}</span>&lt;br/&gt;<br />
ID: <span style="color:blue">{$x.widget_id}</span>&lt;br/&gt;<br />
Price: <span style="color:blue">{$x.widget_price_usd}</span>&lt;br/&gt;<br />
<br />
You can also stash an array of these model objects and loop through the array from the template view:<br />
<br />
...<br />
Widget[] widgets = getWidgets();<br />
<br />
Chunk c = theme.makeChunk("widget_list");<br />
c.set("widgets", widgets);<br />
...<br />
<br />
widget_list.chtml:<br />
<br />
<span style="color:slateblue">{% loop in $widgets as $w %}</span><br />
<span style="color:blue">{$w.widget_id}</span> <span style="color:blue">{$w.widget_name}</span> <span style="color:blue">{$w.widget_price_usd|sprintf($%,.02f)}</span>&lt;br/&gt;<br />
<span style="color:slateblue">{% onEmpty %}</span><br />
No widgets found!<br />
<span style="color:slateblue">{% endloop %}</span><br />
<br />
=== Literals ===<br />
<br />
Don't want tag interpolation? You can mark a whole section of any template to be skipped over by the parser and it will pass through untouched. Just use the following syntax:<br />
<br />
{% literal %}...{% endliteral %}<br />
<br />
Watch out, the surrounding <code>{% literal %}{% endliteral %}</code> marker tags ''will'' get passed into the final output. This is by design, so you don't risk interpreting the literal content on a second or third pass. If you don't want the markers to display in the browser, just place the literal start and end markers inside HTML comments like so:<br />
<br />
&lt;!-- {% literal %} --&gt;<br />
{#example}<br />
{!-- some example --} {$with} {% some %} {.crazy} {+tags} ... {% exec %}{$x = 5}{$title=}Leave Me Intact{=}{% endexec %}<br />
{#}<br />
&lt;!-- {% endliteral %} --&gt;<br />
<br />
Nesting literal blocks is not supported. The inner literal-end marker will be interpreted as the end of the outer literal block.<br />
<br />
=== Backticks (dynamic tags) ===<br />
<br />
Backticks let you use a local tag value to form part of the tag name, on-the-fly. For example, say you're in a loop, and you want to reference some tag data that's specific to one row in the loop, but it's in a tag that was set outside the loop context. Enter backticks. In this example, imagine looping through widgets with IDs ranging from 1-30. In the java code, only the tag {$chosen_widget_27} is set to something non-null.<br />
<br />
<span style="color:green">{#widget_row}</span><br />
<span style="color:blue">{$widget_id}</span> <span style="color:blue">{$widget_name}</span> <span style="color:blue">{$chosen_widget_`$widget_id`|ondefined(SELECTED)}</span><br />
<span style="color:green">{#}</span><br />
<br />
Here is another common use case, working around the fact that include does not expect a dynamic argument:<br />
<br />
<span style="color:slateblue">{% include `$template_name` %}</span><br />
<br />
== Extending Chunk ==<br />
<br />
=== External template repositories ===<br />
<br />
The "include" protocol is available standard, but you can easily whip up<br />
your own class that provides template content. It just has to<br />
implement <code>com.x5.template.ContentSource</code>, which requires two methods,<br />
<code>fetch(...)</code> and <code>getProtocol()</code>.<br />
<br />
For instance, I have a class that fetches html from a wiki. The client<br />
was familiar with creating and editing wiki pages and so instead of<br />
building a content management system for his site content, I just<br />
installed a wiki. The wiki tags look like this: <code>{% wiki.External_Page %}</code><br />
<br />
Here's an example that fetches templates from a database:<br />
<br />
------------------------- DBTemplates.java<br />
<br />
import com.x5.template.ContentSource;<br />
<br />
public class DBTemplates implements ContentSource<br />
{<br />
// ...<br />
<br />
public String fetch(String templateName)<br />
{<br />
return getFromDB(templateName);<br />
}<br />
<br />
public String getProtocol()<br />
{<br />
return "db";<br />
}<br />
<br />
// ...<br />
}<br />
<br />
------------------------- MyApp.java<br />
<br />
// ...<br />
// To provide your chunk with this new source of templates:<br />
<br />
TemplateSet theme = getTheme(); // defined by you<br />
Chunk c = theme.makeChunk("hello#welcome");<br />
c.set("name", getName() );<br />
<br />
DBTemplates dbt = new DBTemplates();<br />
c.addProtocol(dbt);<br />
<br />
return c.toString();<br />
// ...<br />
<br />
------------------------- hello.chtml<br />
<br />
<span style="color:red">{!-- the .include tag will pull from the standard template set<br />
-- in the theme, eg from the filesystem or jar.<br />
-- The db.scores tag will look for a template named "scores" in the database. --}</span><br />
<br />
<span style="color:green">{#welcome}</span><br />
<span style="color:slateblue">{% include top_nav %}</span><br />
<br />
Hello <span style="color:blue">{$name}</span>! Welcome to <span style="color:blue">{$site_name:the site}</span>!&lt;br/&gt;<br />
&lt;br/&gt;<br />
<br />
Here are today's sports scores:&lt;br/&gt;<br />
<span style="color:slateblue">{% db.scores %}</span><br />
<br />
<span style="color:green">{#}</span><br />
<br />
=== Contribute a filter ===<br />
<br />
Another cool way to extend Chunk is to write your own filter to transform tag text within the template. For example, chunk provides no built-in ltrim filter but starting in 1.6, here's how you can supply your own:<br />
<br />
import com.x5.template.Chunk;<br />
import com.x5.template.Theme;<br />
import com.x5.template.filters.BasicFilter;<br />
import com.x5.template.filters.ChunkFilter;<br />
<br />
...<br />
<br />
Theme theme = new theme();<br />
theme.registerFilter( new LeftTrimFilter() );<br />
Chunk c = theme.makeChunk();<br />
c.append("{$name|ltrim}");<br />
c.render( out );<br />
<br />
...<br />
<br />
class LeftTrimFilter extends BasicFilter implements ChunkFilter<br />
{<br />
public String transformText(Chunk chunk, String text, String[] args)<br />
{<br />
if (text == null) return null;<br />
<br />
int i=0;<br />
while (i < text.length() && Character.isWhiteSpace(text.charAt(i))) i++;<br />
<br />
return (i == 0) ? text : text.substring(i);<br />
}<br />
<br />
public String getFilterName()<br />
{<br />
return "ltrim";<br />
}<br />
}<br />
<br />
You may use the Chunk object to reference tag values. Your filter may pass arguments in a function-call style. For example, the built-in filter "indent" can be called with one or two args:<br />
<br />
{$multiline_tag|indent(3,*)}<br />
<br />
In this case, args[0] will contain the entire argument string "3,*" and if your arguments are comma-delimited, args[1] and up will contain the individual arguments. In this example, args[1] will be "3" (number of units to indent) and args[2] "*" (the string to use for indenting - default is a single space " ").<br />
<br />
Note: Exceptions (such as NullPointerException) thrown from user-contributed transformText methods will be caught and recorded to stderr, and the filter will be skipped.<br />
<br />
=== User-Defined filters - exec an "x" template ===<br />
<br />
Starting in 2.0.1, simple macros can be defined and executed via the <code>|filter(#x_template)</code> tag modifier. The value is bound to <code>{$x}</code> and the template gets executed in-place along the filter chain.<br />
<br />
For example, let's say you don't want to go to the trouble of implementing rtrim in java, or you have a format string you use over and over:<br />
<span style="color:green">{#my_template}</span><br />
My dog's name is <span style="color:blue">{% $dog_name|default(Ralph )|filter(#rtrim) %}</span>!<br />
His new collar cost <span style="color:blue">{% $price:4003.2|filter(#currency_us) %}</span> dollars.<br />
<span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for eating trailing whitespace, using a regular expr --}</span><br />
<span style="color:green">{#rtrim}</span><span style="color:blue">{% $x:|s/\s*$// %}</span><span style="color:green">{#}</span><br />
<br />
<span style="color:red">{!-- little macro-filter for formatting currency --}</span><br />
<span style="color:green">{#currency_us}</span><span style="color:slateblue">{% if ($x) %}</span>$ <span style="color:blue">{% $x|sprintf(%,.02f) %}</span><span style="color:slateblue">{% endif %}</span><span style="color:green">{#}</span><br />
<br />
Output:<br />
My dog's name is Ralph!<br />
His new collar cost $ 4,003.20 dollars.<br />
<br />
''Important:'' If the input is null/undefined, the template will still be executed, so be sure to account for that possibility in your #x_template definition.</div>Tom McClure