RenderSession
lets you populate a template and then render it. You obtain a
RenderSession
for a template by calling Template.newRenderSession()
.
Render sessions are meant to be throw-away objects. They should not typically survive
the method in which they are created. A RenderSession
is cheap to instantiate,
but can gain a lot of state as the template gets populated. Therefore, make sure they
don't stick around hogging the heap.
Thead Safety
The RenderSession
class is not thread-safe. However, if different
threads populate different parts of the template, they cannot get in each other's way.
Of course, when using multiple threads to populate a template, the moment at which to
render it needs to be carefully synchronized.
- Author:
- Ayco Holleman
- See Also:
-
Method Summary
Modifier and TypeMethodDescriptionDepopulates and hides the specified nested templates.default RenderSession
Explicitly suppresses the rendering of the specified nested text-only templates.default RenderSession
Enables one or more nested text-only templates.default RenderSession
Enables one or more nested text-only templates.Enables one or more nested text-only templates.enableRecursive
(String... nestedTemplateNames) Recursively enables one or more nested text-only templates.Returns the fully-qualified names of all variables that have not been set yet in the template managed by thisRenderSession
and all templates descending from it.getAllUnsetVariables
(boolean relativePaths) Returns the fully-qualified names of all variables that have not been set yet in the template managed by thisRenderSession
and all templates descending from it.getChildSessions
(String nestedTemplateName) Returns the child sessions that have been created for the specified nested template.Returns the template being populated by thisRenderSession
.Returns all variables that have not been set yet in the template managed by thisRenderSession
.boolean
Returnstrue
if at least one variable has not been set yet in the template managed by thisRenderSession
or any of the templates descending from it.default RenderSession
ifNotSet
(String path, IntFunction<Object> valueGenerator) Sets the specified variable to the value produced by the specifiedIntFunction
if the variable has not already been set.ifNotSet
(String path, VarGroup varGroup, IntFunction<Object> valueGenerator) Sets the specified variable to the value produced by the specifiedIntFunction
if the variable has not already been set.Returns aRenderSession
for the specified nested template.default RenderSession
Populates the template with values extracted from the specified object.Populates the template with values from the specified source data object.default RenderSession
Populates a template nested inside the template managed by thisRenderSession
.default RenderSession
Populates a template nested inside the template managed by thisRenderSession
.populate
(String nestedTemplateName, Object data, String separator, VarGroup varGroup, List<String> names) Populates a template nested inside the template being managed by thisRenderSession
.populateDuo
(String nestedTemplateName, String separator, VarGroup varGroup, List<?> values) Convenience method for populating a nested template that contains exactly two variables.default RenderSession
populateDuo
(String nestedTemplateName, List<?> values) Convenience method for populating a nested template that contains exactly two variables.populateSolo
(String nestedTemplateName, String separator, VarGroup varGroup, List<?> values) Convenience method for populating a nested template that contains exactly one variable.default RenderSession
populateSolo
(String nestedTemplateName, List<?> values) Convenience method for populating a nested template that contains exactly one variable.render()
Renders the template.void
render
(OutputStream out) Renders the template.void
render
(StringBuilder sb) Renders the template.default RenderSession
Causes the specified nested template to become visible and to be repeated the specified number of times.Causes the specified nested template to become visible and to be repeated the specified number of times.default RenderSession
Sets the specified variable to the specified value.Sets the specified variable to the specified value.default RenderSession
setDelayed
(String varName, Supplier<Object> valueGenerator) Sets the specified variable to the value produced by the specifiedSupplier
.setDelayed
(String varName, VarGroup varGroup, Supplier<Object> valueGenerator) Sets the specified variable to the value produced by the specifiedSupplier
.default RenderSession
setPath
(String path, IntFunction<Object> valueGenerator) Sets the value of the specified variable.setPath
(String path, VarGroup varGroup, boolean force, IntFunction<Object> valueGenerator) Sets the value of the specified variable.Unsets the specified variables.
-
Method Details
-
set
Sets the specified variable to the specified value.- Parameters:
varName
- the name of the variable to setvalue
- the value- Returns:
- this
RenderSession
-
set
Sets the specified variable to the specified value. TheStringifier
associated with the specifiedvariable group
will be used to stringify and/or escape the value. If the variable has an inline group name prefix (as in~%html:firstName%
), the variable group specified through the prefix will prevail.- Parameters:
varName
- the name of the variable to setvalue
- the valuevarGroup
- the variable group to assign the variable to if the variable has no group name prefix. May benull
.- Returns:
- this
RenderSession
- See Also:
-
setDelayed
Sets the specified variable to the value produced by the specifiedSupplier
. The supplier'sget()
method will be called each time the template is actually rendered. This may be useful if you plan to render the template multiple times using the sameRenderSession
.- Parameters:
varName
- the name of the variable to setvalueGenerator
- the supplier of the value- Returns:
- this
RenderSession
-
setDelayed
Sets the specified variable to the value produced by the specifiedSupplier
. The supplier'sget()
method will be called eah time the template is actually rendered. This may be useful if you plan to render the template multiple times using the sameRenderSession
.- Parameters:
varName
- the name of the variable to setvarGroup
- the variable group to assign the variable to if the variable has no group name prefix.valueGenerator
- the supplier of the value- Returns:
- this
RenderSession
-
setPath
Sets the value of the specified variable. The variable may be (deeply) nested and is specified using its fully-qualified name. If the variable is a top-level variable (the path consists of just one path segment), this method behaves just likeset(String, Object)
.- Parameters:
path
- a path to a potentially deeply-nested variablevalueGenerator
- a function which is given the array index of the template instance for which to produce a value- Returns:
- this
RenderSession
- See Also:
-
setPath
RenderSession setPath(String path, VarGroup varGroup, boolean force, IntFunction<Object> valueGenerator) Sets the value of the specified variable. The variable may be (deeply) nested. For example:
setPath("companies.departments.employees.firstName", idx -> "John");
This sets the
~%firstName%
variable within theemployees
template within thedepartments
template within thecompanies
template within the main template to "John". Since theemployees
template may already have been instantiated as a repeating template, the value for each instance of the template is set via anIntFunction
. The function is given the array index of the instance and must return the value for that particular instance. For example, if the above statement would populate a cell in an HTML table, which had already been instantiated through other calls on theRenderSession
, then this call:
would put "John0" in the first row of the table, "John1" in the second row of the table, etc.setPath("companies.departments.employees.firstName", idx -> "John" + idx);
If
force
equalsfalse
, then the variable will only be set if the template containing it has already been made visible, for example viarepeat()
orpopulate()
. Ifforce
equalstrue
,setPath()
will itself cause the template to become visible (boilerplate text and all) and the variable will always be set.If the variable is a top-level variable (the path consists of just one path segment), this method behaves like
set(String, Object, VarGroup)
. However, you might still be able to put theIntFunction
to good use:String src = """ ~%%begin:departments% <tr><td>Department number:</td><td>~%deptNo%</td></tr> ~%%end:departments% """; Template template = Template.fromString(src); RenderSession session = template.newRenderSession(); session.repeat("departments", 3).setPath("deptNo", i -> 100 + i).render(); // Output: // <tr><td>Department number:</td><td>100</td></tr> // <tr><td>Department number:</td><td>101</td></tr> // <tr><td>Department number:</td><td>102</td></tr>
- Parameters:
path
- a path to a potentially deeply-nested variablevarGroup
- the variable group to assign the variable to if the variable has no group name prefix.force
- whether to set the variable even if the containing template has not been made visible yet via other meansvalueGenerator
- a function which is given the array index of the template instance for which to produce a value- Returns:
- this
RenderSession
- See Also:
-
ifNotSet
Sets the specified variable to the value produced by the specifiedIntFunction
if the variable has not already been set.- Parameters:
path
- a path to a potentially deeply-nested variablevalueGenerator
- the supplier of the value- Returns:
- this
RenderSession
- See Also:
-
ifNotSet
Sets the specified variable to the value produced by the specifiedIntFunction
if the variable has not already been set. The variable may be (deeply) nested and is therefore specified as a path (likecompanies.departments.employees.firstName
). This method is especially meant to be called after a call toinsert()
orpopulate()
, to set any variables for which the source data object passed to these methods did not provide a value.- Parameters:
path
- a path to a potentially deeply-nested variablevarGroup
- the variable group to assign the variable to if the variable has no group name prefix.valueGenerator
- the supplier of the value- Returns:
- this
RenderSession
- See Also:
-
insert
Populates the template with values extracted from the specified object. If the structure of the object reflects the structure of the template, the template almost literally becomes a "mold" into which to "sink" the object. On the other hand: the object is not required to exactly match the structure of the template. The
accessors
will grab from it what they can from the object and leave the rest alone.Only template variables and nested templates whose name is in the provided
names
array will be populated. Thenames
array is allowed to benull
or empty, in which case an attempt is made to populate the entire template from the source data object.- Parameters:
data
- an object that provides data for all or some of the template variables and nested templates- Returns:
- this
RenderSession
-
insert
Populates the template with values from the specified source data object. Only template variables and nested templates whose name is in the providednames
array will be populated. Thenames
array is allowed to benull
or empty, in which case an attempt is made to populate the entire template from the source data object.- Parameters:
data
- an object that provides data for all or some of the template variables and nested templatesvarGroup
- the variable group to assign the template variables to if they have no inline group name prefix. May benull
.names
- the names of the variables and nested templates that must be populated. May benull
or empty, in which case all variables and nested templates will be checked to see if they can be populated from the specified source data object- Returns:
- this
RenderSession
-
populate
Populates a template nested inside the template managed by thisRenderSession
. The template is populated with values retrieved from the specified source data.- Parameters:
nestedTemplateName
- the name of the nested templatedata
- an object that provides data for all or some of the nested template's variables and nested templates- Returns:
- this
RenderSession
-
populate
Populates a template nested inside the template managed by thisRenderSession
. The template is populated with values retrieved from the specified source data.- Parameters:
nestedTemplateName
- the name of the nested templatedata
- an object that provides data for all or some of the nested template's variables and nested templatesseparator
- the separator to place between instances of the template. May benull
(no separator). The argument is ignored (and may be anything) ifdata
is not aCollection
or an array, or if it is any array orCollection
containing less than two elements- Returns:
- this
RenderSession
-
populate
RenderSession populate(String nestedTemplateName, Object data, String separator, VarGroup varGroup, List<String> names) Populates a template nested inside the template being managed by thisRenderSession
. The template is populated with values retrieved from the specified source data. Only variables and (doubly) nested templates whose names are present in thenames
argument will be populated. Values will be stringified using theStringifier
associated with the specifiedVarGroup
.Repeating Templates
If the specified object is an array or a
Collection
, the template will be repeated for each element in the array orCollection
. This can be used, for example, to generate an HTML table from a nested template that contains just a single<tr>
element.Conditional Rendering
By default, nested templates are not rendered. It takes an explicit call to
repeat()
orpopulate()
to force the template to become visible. However, you can make it more explicit that you do not want the template to become visible. If you pass an empty array or collection to thepopulate()
method, the template is going to be repeated zero times. In other words it will remain invisible. This can be useful in combination with a subsequent call toenable()
orenableRecursive()
.Optionals
It is valid and legitimate to populate a nested template (or the main template for that matter) with an
Optional
.Optional
objects are typically returned from the ubiquitous find-by-id method of a data access object (DAO). If theOptional
is empty, the nested template is explicitly disabled, as though by a call torepeat(nestedTemplateName, 0)
. Otherwise the template is populated with the contents of theOptional
.- Parameters:
nestedTemplateName
- the name of the nested templatedata
- an object that provides data for all or some of the nested template's variables and nested templates. If the object is an array orCollection
, the template will be rendered multiple times, once for each element in the array orCollection
.separator
- the separator to place between instances of the template. May benull
(no separator). The argument is ignored (and may be anything) ifdata
is not aCollection
or an array, or if it is any array orCollection
containing less than two elementsvarGroup
- the variable group to assign the variables to if they have no group name prefix. May benull
.names
- the names of the variables and doubly-nested templates that you want to be populated using the specified data object. May benull
or empty, in which case all variables and nested templates will be checked to see if they can be populated from the specified source data object- Returns:
- this
RenderSession
-
repeat
Causes the specified nested template to become visible and to be repeated the specified number of times.- Parameters:
nestedTemplateName
- the name of the nested templatetimes
- the number of times the template will repeat itself- Returns:
- a
RenderSession
that works on all instances (repetitions) of the nested template - See Also:
-
repeat
Causes the specified nested template to become visible and to be repeated the specified number of times. Iftimes
equals zero, the template is explicitly disabled (in other words: not rendered). Usually it is not necessary to disable a nested template, because nested templates are anyhow only rendered once you populate them. However, it can be useful in combination with a subsequent call toenable()
andenableRecursive()
.Contrary to most of the other methods, this method does not return this
RenderSession
. It is not part of the fluent interface. Instead, it returns a newRenderSession
— a special implementation ofRenderSession
that works on all instances (repetitions) of the nested template. For example, if the nested template contains a variablefoo
and you set its value to "bar", then "bar" will appear in all instances of the template.Beware of the effect of chaining calls to
repeat()
:String src = """ ~%%begin:companies% ~%%begin:departments% ~%%begin:employees% ~%firstName% ~%lastName% ~%%end:employees% ~%%end:departments% ~%%end:companies% """; Template tmpl = Template.fromString(src); RenderSession rs = tmpl.newRenderSession(); rs.repeat("companies", 2) .repeat("departments", 3) .repeat("employees", 4) .set("firstName", "John"); assertEquals(2, rs.getChildSessions("companies").size()); assertEquals(6, rs.in("companies").getChildSessions("departments").size()); assertEquals(24, rs.in("companies").in("departments").getChildSessions("employees").size()); // "John" will appear 24 times
- Parameters:
nestedTemplateName
- the name of the nested templateseparator
- the separator to place between instances of the template. May benull
(no separator). The argument is ignored (and may be anything)times
is less than 2.times
- the number of times the template will repeat itself- Returns:
- a
RenderSession
that works on all instances (repetitions) of the nested template - See Also:
-
in
Returns a
RenderSession
for the specified nested template. If the template has not been instantiated yet, this method behaves as though callingrepeat(nestedTemplateName, 1)
. Otherwise it returns aRenderSession
that works on all instances (repetitions) of the nested template. So, like therepeat()
method, this method does not return thisRenderSession
; it is not part of the fluent interface.Template template = Template.fromString(""" ~%%begin:employees% ~%firstName% ~%lastName% ~%%end:employees% """); RenderSession session = template.newRenderSession(); session.in("employees").set("firstName", "john").set("lastName", "Smith");
The argument is allowed to be a fully-qualified name to a deeply nested template:
String src = """ ~%%begin:companies% ~%%begin:departments% ~%%begin:employees% ~%firstName% ~%lastName% ~%%end:employees% ~%%end:departments% ~%%end:companies% """; Template tmpl = Template.fromString(src); RenderSession rs = tmpl.newRenderSession(); rs.in("companies.departments.employees").set("firstName", "John");
The last statement in the above example is equivalent to:
rs.in("companies").in("departments").in("employees").set("firstName", "John");
- Parameters:
nestedTemplateName
- the name of the nested template- Returns:
- a
RenderSession
that works on all instances (repetitions) of the nested template - See Also:
-
enable
Enables one or more nested text-only templates. That is, their contents will be present in the output of therender()
method. Text-only templates are templates that do not contain template variables or (doubly) nested templates. Equivalent toenable(1, nestedTemplateNames)
.- Parameters:
nestedTemplateNames
- the names of the nested templates to be rendered.- Returns:
- this
RenderSession
-
enable
Enables one or more nested text-only templates. That is, their contents will be present in the output of therender()
method. Text-only templates are templates that do not contain template variables or (doubly) nested templates.You can also use this method to explicitly prevent a nested text-only template from being rendered by specifying
0
(zero) for therepeats
argument. Note, however, that by default template variables and nested templates are not rendered in the first place. You must "do" something to make that happen.Specify an empty
String
array to enable or disable all text-only templates that have not been explicitly enabled or disabled yet. In that case, of course, it does make sense to first disable any text-only templates that should not be rendered.- Parameters:
repeats
- the number of times the nested template(s) must be repeatednestedTemplateNames
- the names of the nested text-only templates to be rendered- Returns:
- this
RenderSession
- See Also:
-
enable
Enables one or more nested text-only templates. That is, their contents will be present in the output of therender()
method. Text-only templates are templates that do not contain template variables or (doubly) nested templates.Specify an empty
String
array to enable all text-only templates that have not been explicitly enabled or disabled yet.- Parameters:
separator
- the separator to place between instances of the template. May benull
(no separator). The argument is ignored (and may be anything) ifrepeats
is less than 2.repeats
- the number of times the nested template(s) must be repeatednestedTemplateNames
- the names of the nested text-only templates to be rendered- Returns:
- this
RenderSession
- See Also:
-
enableRecursive
Recursively enables one or more nested text-only templates. That is, their contents will be present in the output of therender()
method. The nested templates may themselves again contain nested templates, but no template variables are allowed at any nesting level. You may specify an emptyString
array. This will enable all nested text-only templates (at any nesting level) that have not been explicitly disabled yet.- Parameters:
nestedTemplateNames
- the names of the nested text-only templates to be rendered- Returns:
- this
RenderSession
-
disable
Explicitly suppresses the rendering of the specified nested text-only templates. Equivalent to- Parameters:
nestedTemplateNames
-nestedTemplateNames
- the names of the nested text-only templates whose contents to suppress when rendering the parent template- Returns:
- this
RenderSession
-
populateSolo
Convenience method for populating a nested template that contains exactly one variable. The variable may still occur multiple times within the template. The nested template will be repeated for each value in the provided list. In other words, if the list contains one element, the template will be rendered once, with its one and only variable being set to that element.- Parameters:
nestedTemplateName
- the name of the nested template. Must contain exactly one variablevalues
- the value to populate the nested template with- Returns:
- this
RenderSession
-
populateSolo
RenderSession populateSolo(String nestedTemplateName, String separator, VarGroup varGroup, List<?> values) Convenience method for populating a nested template that contains exactly one variable. The variable may still occur multiple times within the template. The nested template will be repeated for each value in the provided list.- Parameters:
nestedTemplateName
- the name of the nested template. Must contain exactly one variableseparator
- the separator to place between instances of the template. May benull
(no separator). The argument is ignored (and may be anything) ifvalues
contains less than two elementsvarGroup
- the variable group to assign the variable to if the variable has no group name prefix.values
- the values to populate the instances of the specified template with- Returns:
- this
RenderSession
-
populateDuo
Convenience method for populating a nested template that contains exactly two variables. The provided list must contain an even number of elements, alternating between a value for the first template variable and a value for the second one. The nested template will be repeated for each consecutive pair of values in the list.- Parameters:
nestedTemplateName
- the name of the nested template.values
- a list of values, alternating between a value for the first template variable and a value for the second one- Returns:
- this
RenderSession
-
populateDuo
RenderSession populateDuo(String nestedTemplateName, String separator, VarGroup varGroup, List<?> values) Convenience method for populating a nested template that contains exactly two variables. The provided list of values must contain an even number of elements, alternating between a value for the first template variable and a value for the second one. The template will be repeated for each pair of values in the list.- Parameters:
nestedTemplateName
- the name of the nested templateseparator
- the separator to place between instances of the template. May benull
(no separator). The argument is ignored (and may be anything) ifvalues
contains zero or two elements.varGroup
- the variable group to assign the variables to if they have no group name prefixvalues
- a list of values, alternating between a value for the first template variable and a value for the second one- Returns:
- this
RenderSession
-
getChildSessions
Returns the child sessions that have been created for the specified nested template. When you populate a nested template, theRenderSession
tacitly spawns a newRenderSession
for that template. For example, thepopulate()
method basically comes down to callinginsert()
on the child session. Since the nested template may be repeating, the parent session actually spawns an array of child sessions. In most cases you don't need to be aware of all this, but if you want full control over what happens in the nested template, you can have it via this method. ARenderException
is thrown if no child sessions have been created yet for the specified nested template.- Parameters:
nestedTemplateName
- the nested template- Returns:
- A
List
of child sessions
-
getUnsetVariables
Returns all variables that have not been set yet in the template managed by thisRenderSession
. Variables in nested templates are not considered.- Returns:
- all variables that have not been set yet in the template managed by this
RenderSession
- See Also:
-
getAllUnsetVariables
Returns the fully-qualified names of all variables that have not been set yet in the template managed by thisRenderSession
and all templates descending from it. Equivalent to callinggetAllUnsetVariables(false)
.- Returns:
- all variables that have not been set yet in the template managed by this
RenderSession
and all templates nested inside it - See Also:
-
getAllUnsetVariables
Returns the fully-qualified names of all variables that have not been set yet in the template managed by this
RenderSession
and all templates descending from it. IfrelativePaths
equalstrue
, the names will be fully-qualified relative to the template being populated by thisRenderSession
. That may not be the root template (the template ultimately being rendered). For example, after a call toin()
orrepeat()
, you will tacitly have descended into a child of theRenderSession
that you originally set up. IfrelativePaths
equalsfalse
, all paths will be absolute, that is, relative to the root template.Beware that there is a certain indeterminacy in the result. One of the (deeply) nested templates may be repeating, and it may have received
Accessor.UNDEFINED
for a variable in one of the repetitions, but a regular value for the same variable in another repetition. In other words, the variable is not set in one repetition of the template, but set in another repetition of the same template. You need to establish whether this can actually happen in your particular case and, if so, whether it has any practical effect. It may still not matter — again, seeAccessor.UNDEFINED
. This method only looks at the first repetition of the template. If you need more precision, you will have to drill down into the child template using methods likein()
orgetChildSessions()
.- Parameters:
relativePaths
- whether to return paths relative to the template being populated by thisRenderSession
- Returns:
- all variables that have not been set yet in the template managed by this
RenderSession
and all templates nested inside it - See Also:
-
hasUnsetVariables
boolean hasUnsetVariables()Returnstrue
if at least one variable has not been set yet in the template managed by thisRenderSession
or any of the templates descending from it. Note that you may not want the template to be fully populated.- Returns:
true
if the template is fully populated
-
unset
Unsets the specified variables. You could also simply set their value to the empty string and get the exact same result when rendering. However, this method more thoroughly unregisters the variables, which will likely affect the outcome of, for example,hasUnsetVariables()
. You can unset deeply nested variables by specifying a path string. For example:unset(companies.departments.name)
.- Parameters:
paths
- the variables to unset- Returns:
- this
RenderSession
-
clear
Depopulates and hides the specified nested templates. Note that you cannot clear the entire session. If that is what you want, you should simply create a new one usingTemplate.newRenderSession()
.- Parameters:
nestedTemplateNames
- the nested templates to depopulate- Returns:
- this
RenderSession
-
render
Renders the template.- Parameters:
out
- the output stream to which to write the populated template
-
render
Renders the template.- Parameters:
sb
- theStringBuilder
to which to write the populated template
-
render
String render()Renders the template.- Returns:
- the populated template (UTF8-encoded)
-
getTemplate
Template getTemplate()Returns the template being populated by thisRenderSession
.- Returns:
- the template being populated by this
RenderSession
-