StringTemplate 4 Documentation

repository·master·Indexed 21 days ago

https://github.com/antlr/stringtemplate4

A Java-based template engine designed for generating formatted text output such as source code, web pages, and emails. It enforces a strict model-view separation and is optimized for multi-targeted code generation. Documentation covers installation via Maven and Gradle, migration from v3 to v4, the use of ModelAdaptors for property mapping, and internal template-to-bytecode mapping.

Tokens
20.8K
Snippets
68
Records
102
Agent score
76%

What's inside StringTemplate 4

  1. What is StringTemplate and how does it work?

    master

    StringTemplate (ST) is a text generation engine designed to enforce strict separation between the model (data) and the view (templates). It works by taking a template—a document containing text and "holes" called attribute expressions—and "rendering" it with specific values.

    By default, attribute expressions are enclosed in angle brackets <attribute-expression>, though these delimiters can be customized. StringTemplate treats everything outside these expressions as literal text.

    In the MVC (Model-View-Controller) pattern:

    • Templates represent the View.
    • Program objects (strings, integers, etc.) represent the Model.
    • The code that pulls from the model and injects attributes into the template represents the Controller.
  2. Understand the core philosophy of StringTemplate

    master

    StringTemplate is designed around the principle of strict model-view separation. Its primary goal is to ensure that the specification of business logic and computation (the model) is completely separated from the specification of how that text is presented or formatted (the view).

    Key characteristics of this approach include:

    • No Side-Effects: StringTemplate does not allow assignments or imperative programming features (like setting variables or arbitrary arithmetic) within templates. This prevents templates from becoming complex, unmaintainable programs.
    • Push Method vs. Pull Method: Unlike many template engines that use a 'pull' method (where the template invokes model methods on demand), StringTemplate uses a push method. All attributes must be computed and pushed into the template before rendering. This eliminates 'order-of-computation' dependencies and prevents bugs where moving a template reference causes null pointer exceptions or missing data.
  3. Avoid mixing inheritance with STGroupDir subdirectories

    master

    StringTemplate enforces a separation between directory-based template management (STGroupDir) and file-based inheritance.

    Constraint: You cannot use the import statement inside a .stg group file if that file is being managed as part of an STGroupDir. Attempting to do so will throw an UnsupportedOperationException.

    Rules of thumb:

    • If using STGroupDir, treat the directory structure as your hierarchy. Do not use import inside files within that directory.
    • If you need inheritance/imports, use STGroupFile for your groups. A group file within an STGroupDir acts like a subdirectory and is subject to the restriction.
  4. How Model Adaptors work in StringTemplate

    master

    StringTemplate typically accesses object properties using the JavaBeans naming pattern (e.g., getFoo()) or via publicly visible fields. If your model uses different naming conventions (e.g., theName()) or has private fields that need to be exposed, you must use a ModelAdaptor.

    A ModelAdaptor<T> is registered for a specific type T. When a template expression attempts to access a property on an object of type T (e.g., <o.foo>), StringTemplate delegates the property lookup to the registered adaptor. The adaptor's job is to map the property name to the actual value in the model.

    // The core interface for custom property resolution
    public interface ModelAdaptor<T> {
        Object getProperty(Interpreter interpreter, ST self, T model, Object property, String propertyName)
            throws STNoSuchPropertyException;
    }
  5. Use the anchor option for aligned line wrapping

    master

    When using wrap, you can use the anchor option to ensure that all wrapped lines align with the left edge of the expression. This is useful for creating clean, indented code structures like arrays or function arguments.

    How it works

    • anchor: When present, StringTemplate lines up all wrapped lines with the left edge of the expression.
    • Indentation vs. Anchor: If both an indentation (from the template context) and an anchor are present, StringTemplate uses whichever is larger.
    • Anchoring to a specific position: Since anchor only works relative to the expression, if you need to anchor to a position to the left of the expression (e.g., to include leading literals in the alignment), wrap the literals and the expression together in an anonymous template <{...}> and apply the anchor to that wrapper.

    Example: Aligned Array

    // Standard wrap (not aligned)
    array(values) ::= <<
    int[] a = { <values; wrap, separator=","> };
    >>
    
    // Aligned wrap using anchor
    array(values) ::= <<
    int[] a = { <values; wrap, anchor, separator=","> };
    >>
    
    // Anchoring including leading literals via anonymous template
    data(a) ::= <<
    int[] a = { <{1,9,2,<values; wrap, separator=",">}; anchor> };
    >>
  6. Understand Auto-indentation bytecode

    master

    StringTemplate handles indentation through specific newline and tab sequences in the bytecode:

    • Standard Expression: <expr> followed by a newline emits expr, write, and newline.
    • Indented Expression: A pattern of \n\t<expr> triggers a newline, an indent "\t" instruction, the expr, a write, and finally a dedent instruction.
  7. Manage templates using STGroupDir and STGroupFile

    master

    StringTemplate provides several ways to organize and load templates:

    1. STGroupDir: Loads templates from a directory. Each .st file in the directory is treated as a template source. This is useful for keeping templates in separate files.
    2. STGroupFile: Loads a single file (typically with a .stg extension) that contains a collection of template definitions. This acts like a single unit or archive of templates.
    3. STRawGroupDir: A variation of STGroupDir used when you want to keep only the template text in your files without formal parameter definitions (e.g., <type> <name>; instead of decl(type, name) ::= "<type> <name>;"). This is often preferred by designers working on HTML or graphics.

    To use these, you create an STGroup object, use getInstanceOf(name) to retrieve a specific template instance, and then proceed with add() and render() as usual.

    // Using STGroupDir
    STGroup group = new STGroupDir("/tmp");
    ST st = group.getInstanceOf("decl");
    st.add("type", "int");
    st.add("name", "x");
    st.add("value", 0);
    String result = st.render();
    
    // Using STGroupFile
    STGroup group = new STGroupFile("/tmp/test.stg");
    ST st = group.getInstanceOf("decl");
    // ... same as above
  8. Understand lazy evaluation in StringTemplate

    master

    StringTemplate uses a lazy evaluation model to decouple the order of attribute computation from the order of text emission.

    In many template engines, the order in which attributes are referenced in the template dictates the order in which the model must compute them. This can create 'dependency hazards' where an attribute $a_i$ depends on $a_j$, and changing the template order causes crashes or incorrect output.

    StringTemplate avoids this by:

    1. Decoupling Controller from View: The controller (your code) can compute attributes in any order convenient for your data structures.
    2. Buffering Attributes: Attributes are stored in built-in attribute tables rather than being immediately evaluated.
    3. Bottom-up Recursive Evaluation: Evaluation is delayed until the controller explicitly renders the root template instance. This invocation performs a bottom-up recursive evaluation of all sub-templates before evaluating the root template itself.

    This allows you to assemble complex templates without worrying about the specific order in which their referenced attributes are computed or rendered.

  9. Distinguish between missing and null attributes

    master

    StringTemplate 4 distinguishes between an attribute being 'missing' and an attribute being 'null'. This distinction is critical when using the null option in templates.

    SituationMeaningResult of <attr; null="foo">
    MissingThe attribute was never added to the template context.Empty string
    NullThe attribute exists in the context but its value is null.foo (the value specified in the null option)
    Non-nullThe attribute exists and has a valid value.The actual value of the attribute
  10. Handle null-valued attributes using the null option

    master

    In StringTemplate 4, you can specify how to handle attributes that exist but have a null value using the null option within a template expression.

    Note that the null option only applies to attributes that are present in the template context but have no value. If an attribute is completely missing (not set at all), the null option has no effect and the expression will result in an empty string.

    ```st
    <name; null="foo">

    Behavior for <name; null="foo">:

    1. Missing: If name does not exist as an attribute $\rightarrow$ yields an empty string.
    2. Null: If name exists but is null $\rightarrow$ yields foo.
    3. Non-null: If name exists and has a value $\rightarrow$ yields the value of name.
  11. Implement group inheritance using the import statement

    master

    You can create variations of a template output (e.g., different language versions or site skins) by creating a 'subgroup' that overrides specific templates from a 'supergroup'.

    To do this, use the import statement within your .stg file to reference the supergroup. When a template is invoked, StringTemplate looks for the definition in the current group first; if not found, it falls back to the imported group. This allows you to only define the templates that actually change between versions, reducing duplication.

    // Java1_5.stg
    import "Java1_4.stg"
     
    /** Override constants from Java1_4.stg */
    constants(typename, names) ::= <<
    public enum <typename> { <names; separator=", "> }
    >>