com.x5.template
Class Chunk

java.lang.Object
  extended by com.x5.template.Chunk
All Implemented Interfaces:
java.util.Map<java.lang.String,java.lang.Object>

public class Chunk
extends java.lang.Object
implements java.util.Map<java.lang.String,java.lang.Object>

Chunk is part Hashtable, part StringBuilder, part find-and-replace.

Assign an initial template (you can stitch on bits of additional template
content as you go) with placeholder tags -- eg {$my_tag} -- and then
set up replacement rules for those tags like so:

    TemplateSet templates = getTemplates(); // defined elsewhere
    Chunk myChunk = templates.makeChunk("my_template");
    myChunk.set("my_tag","hello tag");
    System.out.print( myChunk.toString() );
 

NB: Template {$tags} are bounded by curly brackets, not (parentheses).
Be careful to always close off {#sub_templates}bla bla bla{#} with a single
hash mark surrounded by curly brackets/braces.

TemplateSet is handy if you have a folder with lots of html templates.
Here's an even simpler example, where the template string is supplied
without using TemplateSet:

    String templateBody = "Hello {$name}!  Your balance is ${$balance}."
         + "Pleasure serving you, {$name}!";
    Chunk myChunk = new Chunk();

    // .append() and .set() may be called in any order
    // because tag replacement is delayed until the .toString() call.
    myChunk.append( templateBody );
    myChunk.set("name", user.getName());
    myChunk.set("balance", user.getBalance());
    System.out.println( myChunk.toString() );

    // reset values and re-use -- original templates are not modified
    // when .toString() output is generated.
    myChunk.set("name", user2.getName());
    myChunk.set("balance", user2.getBalance());
    System.out.println( myChunk.toString() );

 

The .toString() method transparently invokes the find and replace
functionality.

FREQUENTLY ASKED QUESTIONS

Q: If I name things just right, will subtemplates get automatically
connected to like-named tags?
ie, if I write a template file like this:

 bla bla bla {$myTemplate} foo foo foo
 {#myTemplate}Hello {$name}!{#}
 

A: No*. To keep things simple and reduce potential for confusion, Chunk
does not auto-magically fill any tags based on naming conventions or
in-template directives. You must explicitly invoke the "include command:
bla bla bla {.include #myTemplate} foo foo foo

* Actually, this documentation is outdated, and several extensions to the
original template syntax are now available:

There is now a powerful new tag modifier syntax which supports:
* providing default values for tags in-template
* automating template placement with "includes"
* in-template text filters including perl-style regex and sprintf
* macro-style templating
* extending the system to access alternate template repositories

Complete details are here: http://www.x5software.com/chunk

Q: My final output says "infinite recursion detected." What gives?

A: You did some variation of this:

   TEMPLATE:
     bla bla bla {$name}
     {#name_info}My name is {$name}{#}

   CODE:
     ...set("name", theme.getSnippet("file#name_info"));
     ...toString();
 

The outer template gets its {$name} tag replaced with "My name is {$name}" --
then, that replacement value is scanned for any tags that might need to be
swapped out for their values. It finds {$name} and, using the rule you gave,
replaces it with "My name is {$name}" so we now have My name is My name is ...
ad infinitum.

This situation is detected by assuming recursion depth will not normally go
deeper than 17. If you legitimately need to nest templates that deep, you
can flatten out the recursion by doing a .toString() expansion partway
through the nest OR you can tweak the depth limit value in the Chunk.java
source code.

Q: Where did my subtemplates go?

A: TemplateSet parses out subtemplates and leaves no trace of them in the
outer template where they were defined. Some people are surprised when
no placeholder tag is automagically generated and left in place of the
subtemplate definition -- sorry, this is not the convention. For optional
elements the template/code usually looks like this:

   TEMPLATE:
     bla bla bla
     {$memberMenu:}
     {#member_menu}this that etc{#}
     foo foo foo

   CODE:
     if (isLoggedIn) {
          myChunk.set("memberMenu", theme.getSnippet("file#member_menu"));
     }
 

The subtemplate does not need to be defined right next to the tag where
it will be used (although that practice does promote readability).

Q: Are tag names and subtemplate names case sensitive?

A: Yes. I prefer to use mixed case in {$tagNames} with first letter
lowercase. In my experience this aids readability since tags are similar
to java variables in concept and that is the java case convention for
variables. Similarly, I prefer lowercase with underscores for all
{#sub_template_names}{#} since templates tend to be defined within html
files which are typically named in all lowercase.

ADVANCED USES

Chunk works great for the simple examples above, but Chunk can do
so much more... the find-and-replace alg is recursive, which is
extremely handy once you get the hang of it.

Internally it resists creating an expensive Hashtable until certain
threshhold conditions are reached.

Output can be constructed on-the-fly with .append() -- say
you're building an HTML table but don't know ahead of time how
many rows and columns it will contain, just loop through all
your data with calls to .append() after preparing each cell and row:

    Chunk table = templates.makeChunk("my_table");
    Chunk rows = templates.makeChunk();
    Chunk row = templates.makeChunk("my_table.my_row");
    Chunk cell = templates.makeChunk("my_table.my_cell");

    rows.set("background_color", getRowColor() );

    while (dataSet.hasMoreData()) {
        DataObj data = dataSet.nextDataObj();
        String[] attributes = data.getAttributes();

        StringBuilder cells = new StringBuilder();
        for (int i=0; i < attributes.length; i++) {
            cell.set("cellContent", attributes[i]);
            cells.append( cell.toString() );
        }

        row.set("name", data.getName());
        row.set("id", data.getID());
        row.set("cells",cells);

        rows.append( row.toString() );
    }

    table.set("table_rows",rows);
    System.out.println( table.toString() );
 
 Possible contents of my_table.html:

 <TABLE>
 {$table_rows}
 </TABLE>

 {#my_row}
 <TR bgcolor="{$background_color}">
  <TD>{$id} - {$name}</TD>
  {$cells}
 </TR>
 {#}

 {#my_cell}
 <TD>{$cell_content}</TD>
 {#}
 
Copyright: waived, free to use
Company: X5 Software
Updates: Chunk Documentation

Version:
3.0.0
Author:
Tom McClure

Nested Class Summary
 
Nested classes/interfaces inherited from interface java.util.Map
java.util.Map.Entry<K,V>
 
Field Summary
static int DEPTH_LIMIT
           
static int HASH_THRESH
           
protected  java.lang.String tagEnd
           
protected  java.lang.String tagStart
           
protected  java.util.Vector<Snippet> template
           
protected  Snippet templateRoot
           
static java.lang.String VERSION
           
 
Constructor Summary
Chunk()
           
 
Method Summary
protected  java.lang.Object _resolveTagValue(SnippetTag tag, int depth, boolean ignoreParentContext)
           
 void addData(DataCapsule smartObj)
          Smart objects implementing DataCapsule can provide their own legend of available tags and which methods to call for exporting the tag data.
 void addData(DataCapsule smartObj, java.lang.String altPrefix)
          Two smart objects of the same type in a single template? No problem.
 void addProtocol(ContentSource src)
           
 void append(Chunk toAdd)
          Add a Chunk on to the end of a Chunk's "template" -- this "child" Chunk won't get it's .toString() invoked until the parent Chunk's tags are replaced, ie when the parent Chunk's .toString() method is invoked.
 void append(Snippet toAdd)
           
 void append(java.lang.String toAdd)
          Add a String on to the end a Chunk's template.
 void clear()
           
 boolean containsKey(java.lang.Object key)
           
 boolean containsValue(java.lang.Object value)
           
 java.util.Set<java.util.Map.Entry<java.lang.String,java.lang.Object>> entrySet()
           
 boolean equals(java.lang.Object o)
           
static java.lang.String findAndReplace(java.lang.String toSearch, java.lang.String find, java.lang.String replace)
          Useful utility function.
 java.lang.Object get(java.lang.Object key)
           
 ChunkFactory getChunkFactory()
           
 ChunkLocale getLocale()
           
 java.util.Map<java.lang.String,java.lang.Object> getTagsTable()
          Retrieve all find-and-replace rules.
 java.lang.Object getTagValue(java.lang.String tagName)
          Retrieves a tag replacement rule.
 java.lang.String getTemplateOrigin()
           
 ContentSource getTemplateSet()
           
 int hashCode()
           
 boolean hasValue(java.lang.String tagName)
           
 boolean isEmpty()
           
 java.util.Set<java.lang.String> keySet()
           
 java.lang.String makeTag(java.lang.String tagName)
           
 java.lang.Object put(java.lang.String key, java.lang.Object value)
           
 void putAll(java.util.Map t)
           
 java.lang.Object remove(java.lang.Object key)
           
 void render(java.io.PrintStream out)
           
 void render(java.io.Writer out)
           
 void render(java.io.Writer out, Chunk context)
           
 void resetTags()
          Clears all tag replacement rules.
 void resetTemplate()
          Clears template
protected  java.lang.Object resolveTagValue(SnippetTag tag, int depth)
           
protected  java.lang.Object resolveTagValue(SnippetTag tag, int depth, java.lang.String origin)
           
protected  java.lang.Object resolveTagValue(java.lang.String tagName, int depth)
           
 void set(java.lang.String tagName)
          For convenience, sets a flag to "TRUE" - to reverse, call unset("flag")
 void set(java.lang.String tagName, char tagValue)
          For convenience, auto-converts char to String and creates tag replacement rule.
 void set(java.lang.String tagName, Chunk tagValue)
          Creates a find-and-replace rule for tag replacement.
 void set(java.lang.String tagName, int tagValue)
          For convenience, auto-converts int to String and creates tag replacement rule.
 void set(java.lang.String tagName, long tagValue)
          For convenience, auto-converts long to String and creates tag replacement rule.
 void set(java.lang.String tagName, java.lang.Object tagValue)
          Convenience method, chains to set(String s, Object o, String ifNull)
 void set(java.lang.String tagName, java.lang.Object tagValue, java.lang.String ifNull)
          Create a tag replacement rule, supplying a default value in case the value passed is null.
 void set(java.lang.String tagName, java.lang.String tagValue)
          Creates a find-and-replace rule for tag replacement.
 void set(java.lang.String tagName, java.lang.StringBuffer tagValue)
          For convenience, auto-converts StringBuffer to String and creates tag replacement rule.
 void set(java.lang.String tagName, java.lang.StringBuilder tagValue)
          For convenience, auto-converts StringBuilder to String and creates tag replacement rule.
 void setChunkFactory(ChunkFactory factory)
           
 void setErrorHandling(boolean renderErrs, java.io.PrintStream err)
           
 void setLiteral(java.lang.String tagName, java.lang.String literalValue)
          setLiteral() tag values will render verbatim, so even if the value contains tags/specials they will not be expanded.
 void setLocale(ChunkLocale chunkLocale)
           
 void setLocale(java.util.Locale javaLocale)
           
 void setLocale(java.lang.String localeCode)
           
 void setMultiple(Chunk copyFrom)
          Adds multiple find-and-replace rules using all rules from the passed Chunk.
 void setMultiple(java.util.Map<java.lang.String,java.lang.Object> rules)
          Adds multiple find-and-replace rules using all entries in the Hashtable.
 void setOrDelete(java.lang.String tagName, java.lang.Object tagValue)
          Careful, setOrDelete will DELETE a previous value for the tag at this level if passed a null value.
 void setToBean(java.lang.String tagName, java.lang.Object bean)
          Make bean properties available to template
 void setToBean(java.lang.String tagName, java.lang.Object bean, java.lang.String ifNull)
          Make bean properties available to template
 int size()
           
 boolean stillNeeds(java.lang.String tagName)
           
 java.lang.String toString()
          Apply all tag replacement rules recursively and return template contents with translated tags.
 java.lang.String toString(Chunk context)
           
 void unset(java.lang.String tagName)
          unset("tag") deletes the named tag expansion rule from the ruleset.
 java.util.Collection<java.lang.Object> values()
           
 
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
 

Field Detail

HASH_THRESH

public static final int HASH_THRESH
See Also:
Constant Field Values

DEPTH_LIMIT

public static final int DEPTH_LIMIT
See Also:
Constant Field Values

VERSION

public static final java.lang.String VERSION
See Also:
Constant Field Values

templateRoot

protected Snippet templateRoot

template

protected java.util.Vector<Snippet> template

tagStart

protected java.lang.String tagStart

tagEnd

protected java.lang.String tagEnd
Constructor Detail

Chunk

public Chunk()
Method Detail

getTemplateSet

public ContentSource getTemplateSet()

setChunkFactory

public void setChunkFactory(ChunkFactory factory)

getChunkFactory

public ChunkFactory getChunkFactory()

append

public void append(Snippet toAdd)

append

public void append(java.lang.String toAdd)
Add a String on to the end a Chunk's template.


append

public void append(Chunk toAdd)
Add a Chunk on to the end of a Chunk's "template" -- this "child" Chunk won't get it's .toString() invoked until the parent Chunk's tags are replaced, ie when the parent Chunk's .toString() method is invoked. The child does NOT get cloned in this call so if you append a chunk, alter its tags and append it again, you'll see it in its final state twice after the template expansion. To keep incremental changes, use append(child.toString()) instead.


set

public void set(java.lang.String tagName,
                java.lang.String tagValue)
Creates a find-and-replace rule for tag replacement. Overwrites any previous rules for this tagName. Do not include the tag boundary markers in the tagName, ie GOOD: set("this", "that") BAD: set("{$this}", "that")

Parameters:
tagName - will be ignored if null.
tagValue - will be translated to the empty String if null -- use setOrDelete() instead of set() if you don't need/want this behavior.

set

public void set(java.lang.String tagName,
                Chunk tagValue)
Creates a find-and-replace rule for tag replacement. See remarks at append(Chunk c).

Parameters:
tagName - will be ignored if null.
tagValue - will be translated to the empty String if null.
See Also:
append(Chunk)

set

public void set(java.lang.String tagName,
                java.lang.Object tagValue)
Convenience method, chains to set(String s, Object o, String ifNull)


setOrDelete

public void setOrDelete(java.lang.String tagName,
                        java.lang.Object tagValue)
Careful, setOrDelete will DELETE a previous value for the tag at this level if passed a null value. This is a way around the standard behavior which interprets null values as the empty string. The default is to use the empty string for null object/strings. setOrDelete provides an alternate option, leaving the tag "unset" in case the value is null, which allows the template to provide its own default value.


setLiteral

public void setLiteral(java.lang.String tagName,
                       java.lang.String literalValue)
setLiteral() tag values will render verbatim, so even if the value contains tags/specials they will not be expanded. However, if the final engine output is passed back into the chunk processor as a static string (for example, using chunk to pre-generate table rows that are destined for placement in another chunk), it will not be protected from the engine in that second pass. To prevent re-processing higher up the chain, encase your string in {% literal %}...{% endliteral %} tags, with the tradeoff that they will appear in your final output. This is by design, so the literal will be preserved even after multiple passes through the engine. Typical workaround: ... Or, just be super-careful to use setLiteral() again when placing pre-processed output into higher-level chunks.

Parameters:
tagName -
literalValue -

set

public void set(java.lang.String tagName,
                java.lang.Object tagValue,
                java.lang.String ifNull)
Create a tag replacement rule, supplying a default value in case the value passed is null. If both the tagValue and the fallback are null, the rule created will resolve all instances of the tag to the string "NULL"

Parameters:
tagName - tag to replace
tagValue - replacement value -- no-op unless this is of type String or Chunk.
ifNull - fallback replacement value in case tagValue is null

setToBean

public void setToBean(java.lang.String tagName,
                      java.lang.Object bean)
Make bean properties available to template


setToBean

public void setToBean(java.lang.String tagName,
                      java.lang.Object bean,
                      java.lang.String ifNull)
Make bean properties available to template


set

public void set(java.lang.String tagName)
For convenience, sets a flag to "TRUE" - to reverse, call unset("flag")


set

public void set(java.lang.String tagName,
                int tagValue)
For convenience, auto-converts int to String and creates tag replacement rule. Overwrites any existing rule with this tagName.


set

public void set(java.lang.String tagName,
                char tagValue)
For convenience, auto-converts char to String and creates tag replacement rule. Overwrites any existing rule with this tagName.


set

public void set(java.lang.String tagName,
                long tagValue)
For convenience, auto-converts long to String and creates tag replacement rule. Overwrites any existing rule with this tagName.


set

public void set(java.lang.String tagName,
                java.lang.StringBuilder tagValue)
For convenience, auto-converts StringBuilder to String and creates tag replacement rule. Overwrites any existing rule with this tagName.


set

public void set(java.lang.String tagName,
                java.lang.StringBuffer tagValue)
For convenience, auto-converts StringBuffer to String and creates tag replacement rule. Overwrites any existing rule with this tagName.


unset

public void unset(java.lang.String tagName)
unset("tag") deletes the named tag expansion rule from the ruleset.

Parameters:
tagName -

hasValue

public boolean hasValue(java.lang.String tagName)
Returns:
true if a rule exists for this tagName, otherwise false. Returns false if tagName is null.

stillNeeds

public boolean stillNeeds(java.lang.String tagName)
Returns:
true if a rule does not yet exist for this tagName, otherwise false. Returns false if tagName is null.

toString

public java.lang.String toString()
Apply all tag replacement rules recursively and return template contents with translated tags. Rules and original template pieces remain intact, so toString can be called several times, modifying rules between each invocation to produce a slightly different output each time.

Overrides:
toString in class java.lang.Object
Returns:
A String with all template pieces assembled and all known tags recursively resolved.

toString

public java.lang.String toString(Chunk context)

render

public void render(java.io.PrintStream out)
            throws java.io.IOException
Throws:
java.io.IOException

render

public void render(java.io.Writer out)
            throws java.io.IOException
Throws:
java.io.IOException

render

public void render(java.io.Writer out,
                   Chunk context)
            throws java.io.IOException
Throws:
java.io.IOException

getTagValue

public java.lang.Object getTagValue(java.lang.String tagName)
Retrieves a tag replacement rule. getTagValue() responds outside the context of recursive tag replacement, so the return value may include unresolved tags. To iterate up the ancestor chain, use get() instead.

Returns:
The Chunk or Snippet etc. that this tag will resolve to, or null if no rule yet exists.

addProtocol

public void addProtocol(ContentSource src)

resolveTagValue

protected java.lang.Object resolveTagValue(SnippetTag tag,
                                           int depth,
                                           java.lang.String origin)

resolveTagValue

protected java.lang.Object resolveTagValue(SnippetTag tag,
                                           int depth)

_resolveTagValue

protected java.lang.Object _resolveTagValue(SnippetTag tag,
                                            int depth,
                                            boolean ignoreParentContext)

resolveTagValue

protected java.lang.Object resolveTagValue(java.lang.String tagName,
                                           int depth)

resetTags

public void resetTags()
Clears all tag replacement rules.


clear

public void clear()
Specified by:
clear in interface java.util.Map<java.lang.String,java.lang.Object>

resetTemplate

public void resetTemplate()
Clears template


containsKey

public boolean containsKey(java.lang.Object key)
Specified by:
containsKey in interface java.util.Map<java.lang.String,java.lang.Object>

containsValue

public boolean containsValue(java.lang.Object value)
Specified by:
containsValue in interface java.util.Map<java.lang.String,java.lang.Object>

entrySet

public java.util.Set<java.util.Map.Entry<java.lang.String,java.lang.Object>> entrySet()
Specified by:
entrySet in interface java.util.Map<java.lang.String,java.lang.Object>

equals

public boolean equals(java.lang.Object o)
Specified by:
equals in interface java.util.Map<java.lang.String,java.lang.Object>
Overrides:
equals in class java.lang.Object

get

public java.lang.Object get(java.lang.Object key)
Specified by:
get in interface java.util.Map<java.lang.String,java.lang.Object>

hashCode

public int hashCode()
Specified by:
hashCode in interface java.util.Map<java.lang.String,java.lang.Object>
Overrides:
hashCode in class java.lang.Object

isEmpty

public boolean isEmpty()
Specified by:
isEmpty in interface java.util.Map<java.lang.String,java.lang.Object>

keySet

public java.util.Set<java.lang.String> keySet()
Specified by:
keySet in interface java.util.Map<java.lang.String,java.lang.Object>

put

public java.lang.Object put(java.lang.String key,
                            java.lang.Object value)
Specified by:
put in interface java.util.Map<java.lang.String,java.lang.Object>

remove

public java.lang.Object remove(java.lang.Object key)
Specified by:
remove in interface java.util.Map<java.lang.String,java.lang.Object>

putAll

public void putAll(java.util.Map t)
Specified by:
putAll in interface java.util.Map<java.lang.String,java.lang.Object>

size

public int size()
Specified by:
size in interface java.util.Map<java.lang.String,java.lang.Object>

values

public java.util.Collection<java.lang.Object> values()
Specified by:
values in interface java.util.Map<java.lang.String,java.lang.Object>

setMultiple

public void setMultiple(java.util.Map<java.lang.String,java.lang.Object> rules)
Adds multiple find-and-replace rules using all entries in the Hashtable. Replaces an existing rule if tagNames collide.


setMultiple

public void setMultiple(Chunk copyFrom)
Adds multiple find-and-replace rules using all rules from the passed Chunk. Replaces any existing rules with the same tagName.


getTagsTable

public java.util.Map<java.lang.String,java.lang.Object> getTagsTable()
Retrieve all find-and-replace rules. Alterations to the returned Hashtable WILL AFFECT the tag replacement rules of the Chunk directly. Does not return a clone.

Returns:
a Hashtable containing the Chunk's find-and-replace rules.

addData

public void addData(DataCapsule smartObj)
Smart objects implementing DataCapsule can provide their own legend of available tags and which methods to call for exporting the tag data. NB: set() won't unwrap its data until "runtime" ie when toString() is called. addData() is not like that -- the exported values are copied and frozen when the addData() call is made. No pointer to the DataCapsule is kept.

Parameters:
smartObj -

addData

public void addData(DataCapsule smartObj,
                    java.lang.String altPrefix)
Two smart objects of the same type in a single template? No problem. Provide a unique altPrefix for each one to avoid tag namespace collisions. Note: these two calls are now equivalent: chunk.addData(capsule,"x"); chunk.set("x",capsule); The second form is preferred, so this method may get deprecated.

Parameters:
smartObj -
altPrefix -

makeTag

public java.lang.String makeTag(java.lang.String tagName)

setErrorHandling

public void setErrorHandling(boolean renderErrs,
                             java.io.PrintStream err)

setLocale

public void setLocale(java.lang.String localeCode)

setLocale

public void setLocale(java.util.Locale javaLocale)

setLocale

public void setLocale(ChunkLocale chunkLocale)

getLocale

public ChunkLocale getLocale()

findAndReplace

public static java.lang.String findAndReplace(java.lang.String toSearch,
                                              java.lang.String find,
                                              java.lang.String replace)
Useful utility function. An efficient find-and-replace-all algorithm for simple cases when regexp would be overkill. IMO they should have included this with String.

Parameters:
toSearch - text body.
find - text to search for.
replace - text to insert in place of "find" -- defaults to empty String if null is passed.
Returns:
a new String based on input with all instances of "find" replaced with "replace"

getTemplateOrigin

public java.lang.String getTemplateOrigin()


Copyright © 2015. All Rights Reserved.