Interface RenderSession


public sealed interface RenderSession
A 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 Details

    • set

      default RenderSession set(String varName, Object value)
      Sets the specified variable to the specified value.
      Parameters:
      varName - the name of the variable to set
      value - the value
      Returns:
      this RenderSession
    • set

      RenderSession set(String varName, Object value, VarGroup varGroup)
      Sets the specified variable to the specified value. The Stringifier associated with the specified variable 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 set
      value - the value
      varGroup - the variable group to assign the variable to if the variable has no group name prefix. May be null.
      Returns:
      this RenderSession
      See Also:
    • setDelayed

      default RenderSession setDelayed(String varName, Supplier<Object> valueGenerator)
      Sets the specified variable to the value produced by the specified Supplier. The supplier's get() 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 same RenderSession.
      Parameters:
      varName - the name of the variable to set
      valueGenerator - the supplier of the value
      Returns:
      this RenderSession
    • setDelayed

      RenderSession setDelayed(String varName, VarGroup varGroup, Supplier<Object> valueGenerator)
      Sets the specified variable to the value produced by the specified Supplier. The supplier's get() 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 same RenderSession.
      Parameters:
      varName - the name of the variable to set
      varGroup - 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

      default RenderSession setPath(String path, IntFunction<Object> valueGenerator)
      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 like set(String, Object).
      Parameters:
      path - a path to a potentially deeply-nested variable
      valueGenerator - 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 the employees template within the departments template within the companies template within the main template to "John". Since the employees template may already have been instantiated as a repeating template, the value for each instance of the template is set via an IntFunction. 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 the RenderSession, then this call:

      
       setPath("companies.departments.employees.firstName", idx -> "John" + idx);
       
      would put "John0" in the first row of the table, "John1" in the second row of the table, etc.

      If force equals false, then the variable will only be set if the template containing it has already been made visible, for example via repeat() or populate(). If force equals true, 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 the IntFunction 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 variable
      varGroup - 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 means
      valueGenerator - a function which is given the array index of the template instance for which to produce a value
      Returns:
      this RenderSession
      See Also:
    • ifNotSet

      default RenderSession ifNotSet(String path, IntFunction<Object> valueGenerator)
      Sets the specified variable to the value produced by the specified IntFunction if the variable has not already been set.
      Parameters:
      path - a path to a potentially deeply-nested variable
      valueGenerator - the supplier of the value
      Returns:
      this RenderSession
      See Also:
    • ifNotSet

      RenderSession ifNotSet(String path, VarGroup varGroup, IntFunction<Object> valueGenerator)
      Sets the specified variable to the value produced by the specified IntFunction if the variable has not already been set. The variable may be (deeply) nested and is therefore specified as a path (like companies.departments.employees.firstName). This method is especially meant to be called after a call to insert() or populate(), 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 variable
      varGroup - 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

      default RenderSession insert(Object data)

      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. The names array is allowed to be null 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

      RenderSession insert(Object data, VarGroup varGroup, List<String> names)
      Populates the template with values from the specified source data object. Only template variables and nested templates whose name is in the provided names array will be populated. The names array is allowed to be null 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
      varGroup - the variable group to assign the template variables to if they have no inline group name prefix. May be null.
      names - the names of the variables and nested templates that must be populated. May be null 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

      default RenderSession populate(String nestedTemplateName, Object data)
      Populates a template nested inside the template managed by this RenderSession. The template is populated with values retrieved from the specified source data.
      Parameters:
      nestedTemplateName - the name of the nested template
      data - an object that provides data for all or some of the nested template's variables and nested templates
      Returns:
      this RenderSession
    • populate

      default RenderSession populate(String nestedTemplateName, Object data, String separator)
      Populates a template nested inside the template managed by this RenderSession. The template is populated with values retrieved from the specified source data.
      Parameters:
      nestedTemplateName - the name of the nested template
      data - an object that provides data for all or some of the nested template's variables and nested templates
      separator - the separator to place between instances of the template. May be null (no separator). The argument is ignored (and may be anything) if data is not a Collection or an array, or if it is any array or Collection 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 this RenderSession. The template is populated with values retrieved from the specified source data. Only variables and (doubly) nested templates whose names are present in the names argument will be populated. Values will be stringified using the Stringifier associated with the specified VarGroup.

      Repeating Templates

      If the specified object is an array or a Collection, the template will be repeated for each element in the array or Collection. 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() or populate() 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 the populate() 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 to enable() or enableRecursive().

      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 the Optional is empty, the nested template is explicitly disabled, as though by a call to repeat(nestedTemplateName, 0). Otherwise the template is populated with the contents of the Optional.

      Parameters:
      nestedTemplateName - the name of the nested template
      data - an object that provides data for all or some of the nested template's variables and nested templates. If the object is an array or Collection, the template will be rendered multiple times, once for each element in the array or Collection.
      separator - the separator to place between instances of the template. May be null (no separator). The argument is ignored (and may be anything) if data is not a Collection or an array, or if it is any array or Collection containing less than two elements
      varGroup - the variable group to assign the variables to if they have no group name prefix. May be null.
      names - the names of the variables and doubly-nested templates that you want to be populated using the specified data object. May be null 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

      default RenderSession repeat(String nestedTemplateName, int times)
      Causes the specified nested template to become visible and to be repeated the specified number of times.
      Parameters:
      nestedTemplateName - the name of the nested template
      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:
    • repeat

      RenderSession repeat(String nestedTemplateName, String separator, int times)
      Causes the specified nested template to become visible and to be repeated the specified number of times. If times 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 to enable() and enableRecursive().

      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 new RenderSession — a special implementation of RenderSession that works on all instances (repetitions) of the nested template. For example, if the nested template contains a variable foo 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 template
      separator - the separator to place between instances of the template. May be null (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

      RenderSession in(String nestedTemplateName)

      Returns a RenderSession for the specified nested template. If the template has not been instantiated yet, this method behaves as though calling repeat(nestedTemplateName, 1). Otherwise it returns a RenderSession that works on all instances (repetitions) of the nested template. So, like the repeat() method, this method does not return this RenderSession; 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

      default RenderSession enable(String... nestedTemplateNames)
      Enables one or more nested text-only templates. That is, their contents will be present in the output of the render() method. Text-only templates are templates that do not contain template variables or (doubly) nested templates. Equivalent to enable(1, nestedTemplateNames).
      Parameters:
      nestedTemplateNames - the names of the nested templates to be rendered.
      Returns:
      this RenderSession
    • enable

      default RenderSession enable(int repeats, String... nestedTemplateNames)
      Enables one or more nested text-only templates. That is, their contents will be present in the output of the render() 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 the repeats 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 repeated
      nestedTemplateNames - the names of the nested text-only templates to be rendered
      Returns:
      this RenderSession
      See Also:
    • enable

      RenderSession enable(String separator, int repeats, String... nestedTemplateNames)
      Enables one or more nested text-only templates. That is, their contents will be present in the output of the render() 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 be null (no separator). The argument is ignored (and may be anything) if repeats is less than 2.
      repeats - the number of times the nested template(s) must be repeated
      nestedTemplateNames - the names of the nested text-only templates to be rendered
      Returns:
      this RenderSession
      See Also:
    • enableRecursive

      RenderSession enableRecursive(String... nestedTemplateNames)
      Recursively enables one or more nested text-only templates. That is, their contents will be present in the output of the render() method. The nested templates may themselves again contain nested templates, but no template variables are allowed at any nesting level. You may specify an empty String 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

      default RenderSession disable(String... nestedTemplateNames)
      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

      default RenderSession populateSolo(String nestedTemplateName, 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. 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 variable
      values - 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 variable
      separator - the separator to place between instances of the template. May be null (no separator). The argument is ignored (and may be anything) if values contains less than two elements
      varGroup - 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

      default RenderSession populateDuo(String nestedTemplateName, List<?> values)
      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 template
      separator - the separator to place between instances of the template. May be null (no separator). The argument is ignored (and may be anything) if values contains zero or two elements.
      varGroup - the variable group to assign the variables to if they have no group name prefix
      values - a list of values, alternating between a value for the first template variable and a value for the second one
      Returns:
      this RenderSession
    • getChildSessions

      List<RenderSession> getChildSessions(String nestedTemplateName)
      Returns the child sessions that have been created for the specified nested template. When you populate a nested template, the RenderSession tacitly spawns a new RenderSession for that template. For example, the populate() method basically comes down to calling insert() 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. A RenderException 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

      List<String> getUnsetVariables()
      Returns all variables that have not been set yet in the template managed by this RenderSession. 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

      List<String> 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. Equivalent to calling getAllUnsetVariables(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

      List<String> getAllUnsetVariables(boolean relativePaths)

      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. If relativePaths equals true, the names will be fully-qualified relative to the template being populated by this RenderSession. That may not be the root template (the template ultimately being rendered). For example, after a call to in() or repeat(), you will tacitly have descended into a child of the RenderSession that you originally set up. If relativePaths equals false, 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, see Accessor.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 like in() or getChildSessions().

      Parameters:
      relativePaths - whether to return paths relative to the template being populated by this RenderSession
      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()
      Returns true if at least one variable has not been set yet in the template managed by this RenderSession 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

      RenderSession unset(String... paths)
      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

      RenderSession clear(String... nestedTemplateNames)
      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 using Template.newRenderSession().
      Parameters:
      nestedTemplateNames - the nested templates to depopulate
      Returns:
      this RenderSession
    • render

      void render(OutputStream out)
      Renders the template.
      Parameters:
      out - the output stream to which to write the populated template
    • render

      void render(StringBuilder sb)
      Renders the template.
      Parameters:
      sb - the StringBuilder 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 this RenderSession.
      Returns:
      the template being populated by this RenderSession