StrataCode Template Language

The template language provides the power of StrataCode when you have a program which is more text than code. While you can keep using your existing template language, there are several reasons you might want to look StrataCode's template language.

  • IDE support for editing
  • Flexibly control whether template sections are reactive or rendered in normal imperative code,
  • Customize templates by adding layers
  • Debug complex dynamic templates
  • Customize StrataCode's code-templates for adding a framework layer

The template language itself is customized by a framework when it's associated with a particular file extension (e.g. schtml, scss, sct, scxml, scsh etc). Of these formats, schtml and scss extend the template language grammar to offer parsing of the text sections. You can configure many aspects of how the template language behaves:

  • whether the template is compiled into a Java .class or interpreted at build or runtime
  • the default base class for the template if one is not specified
  • the code generated for the "output" method used to retrieve the template's current value
  • the suffix of the generated file
  • enable "stateful templates" - where you can incrementally update the template based on property changes and data binding
  • whether the template by default modifies or replaces a previous template with the same name
  • whether the template is named by it's path name in the layer or by it's type name in the language system

Framework developers may use the template language for code snippet templates. They also may use the template language to generate xml and other build files reducing the copying of project configuration.

Some web frameworks use a pattern called "code behind" where you write a class to define a page object that sits behind the template. The properties and methods in the page object are available to the template. StrataCode takes this pattern further. It let's you weave code files, and template files in any number, each one using, extending, or modifying the previous one.

Comparison to JSP

JSP is built by first parsing JSP, then generating Java, then parsing the Java. StrataCode templates are parsed in one-pass, then that model is transformed to Java without a reparsing step. The one-pass model produces statically typed, well structured templates with the same tooling potential as Java. It's also more flexible, letting you basically put template free-form text in any situation where you might expect a StringLiteral in Java.

The grammar for the template language is derived from the StrataCode grammar in just a few dozen lines of code. The parsing rules are a bit more restrictive in some cases than JSP but the restrictions make for more readable, well structured templates. They have good error messages and have the static typing as in Java.

While JSP has include tags, tag libraries and more, the template language takes a simpler approach, leveraging concepts from Java already for composition, typing etc. While JSP tries to hide the complexity of Java via a myriad of tags, operators, attributes etc. the template language just lets you mix template strings with Java code. You use Java operators for Java-like things. Of course in general, you should try as hard as possible to keep your templates simple - to keep Java separate from your template code so others can read and modify it easily. But when you do need to add properties, write a template macros, extend another template, etc. there's really no need to invent new syntax and structure when Java does everything you need in a simpler, more flexible way.

Template Language Types

The schtml file uses the HTMLLanguage which extends the TemplateLanguage grammar. It supports all of the features of the Template language but lets you generate and update HTML on the client, server or both.

The sct file works just like an sc file except that it starts out as a template. Use that for any sc file which requires some long strings which you do not want to escape. If you want to separate those strings from the code entirely, you can put the sct file in a different layer.

The scxml file generates an xml file typically at build time. The system is designed to make it easy to parse specific types of XML files and produce read-write code models of those types so you can do awesome integrations with systems based on XML.

Template Operators

The template language uses the same basic operators as JSP but unlike JSP they cannot be mixed freely with Java. They are only used to separate the main constructs: definitions, statements, and expressions. StrataCode templates work like StrataCode definitions that start out defining a content string. With the <% operator, you break into the context of code and with %> you return to the string. This string gets added to a string builder in an output method:

file: doc/core/Hello.schtml
Hello World
turns into:
class Hello {
   public StringBuilder output() {
      StringBuilder out = new StringBuilder();
      out.append("Hello World");
      return out;
   }
}
There are three escapes taken from JSP: <% %> surrounds Java statements that get put into the current method. <%= java-expression %> defines an expression to append to the out variable. <%= content %> is equivalent to <% out.append("content") %>, <%! %> defines the declarations chunk for this class. The first declaration in the template file is treated differently if it is a class or a modify declaration. In that case, it defines the class used to hold that output method. The type of this class may define a template which affects how that class works as well. So for example a Servlet's output method will take the request and response parameters so they can be used in the template page.
file: doc/core/Hello.schtml
<%! class Hello {
   String message = "Hello World";
} %>
<%= message %>
This defines a class named Hello with a single message instance variable. When the template is evaluated that message is output.
file: doc/core/Hello.schtml
<%! Hello {
   message = "Hello from another Layer";
} %>
<%= super.output() %>
Now in a separate layer you modify the class Hello and override the initializer for the message object. By default, the modified template generates a new output method that overrides the one in the previous class. To include the content from the previous template, you have to evaluate super.output. The advantage is that you can place content before and after the previous template's content. Some examples:
<%= foo + "bar" %>
The expression inside of the template is evaluated and the string value is added to the templates value.
<% for (int i = 0; i < 10; i++) {%>
      element <%= i %>
<% }%>
<% lets you inject one or more Java statements into the currently executing method. You also use this to go back StrataCode code after completing a template definition (e.g. <% } %> or <% ) %>).
<%! int memberVariable; %>
This defines sc constructs and all <%! tags must appear before any other <% tags. If the first definition is a class, object or modify definition it defines the StrataCode class for the template. Just as with Java and StrataCode files, the type name matches the filename. So if your file is called "foo.sctd" your first definition might look like:
<%! class foo extends bar { %>
   This text is part of the template
<% } %>
The generated "foo" class now will extend some base class bar, letting you use methods or fields of bar in your template. The content inside of the definitions is automatically put into the "StringBuilder output()" method. You can layer template files just like you layer StrataCode and Java files. By default, your template will just replace the template in the previous layer. But if the class declaration for the template is a modify operation, you can use the previous template's definition to define the new one. The declarations section also can be used to define methods inside of the template. Each method can have its own template, allowing you to define multiple parameterized methods in the template file:
file: doc/core/foo.sct
<%! public String header(String title, String fromFile) { %>
   Template language text and operators which get evaluated and returned from this method
<% } %>
<%! public String footer() { %>
   template text - same file, different method
<% } %>
or the same thing:
file: doc/core/foo.sct
<%! public String header(String title, String fromFile) { %>
   Template language text and operators which get evaluated and returned from this method
<% } public String footer() { %>
   template text - same file, different method
<% } %>
If this appears as the first definition in the file, it is used as the definition for the StrataCode class which contains the template. So if your file is called "foo.sctd" your first definition might look like:
<%! class foo extends bar { %>
   This text is part of the template
<% } %>
The generated "foo" class now will extend some base class bar, letting you use methods or fields of bar in your template. The content inside of the definitions is automatically put into the "getFoo()" method which is generated from the template if you compile it. Of course templates can be layered so you could modify or replace one template by a subsequent template in the layer. If that header method is defined in a file called "util", you'd refer to it from another template like this:
file: doc/core/foo.sct
<%= util.header("Getting Started","gettingStarted") %>
or if you wanted to pass a template to that method you'd use:
file: doc/core/foo.sct
<%= util.header(%>Getting Started<%,"gettingStarted") %>
and extra nesting levels work but hopefully you can avoid this in practice:
file: doc/core/foo.sct
<%= util.header(%>Getting <%="Start"%>ed<%,"gettingStarted") %>
The template mechanism lets you mix Java definitions with template definitions as long as the context on both sides match up. A template definition can occur anyplace you'd expect a String value in Java so in the above example we can switch specify the first string parameter to the util.header method as a template definition. Note also that you can break up a Java expression, statement or declaration into pieces joined together by a template definition. For example, the template body can occupy the entire method body which is a short hand for a method which returns the template as a String. And of course a template definition can have nested Java expressions. A variable can also be defined from a template:
<% String myStr = %>Now in this template<%;%>
A template can extend or modify a StrataCode or java file in a previous layer or vice versa. For example, a simple parameterized template might look like:
file: doc/core/formatter.schtml
<%! String firstName, lastName; %>
Name: <%= lastName %>, <%= firstName %>
In a sublayer you could modify the behavior of that template by changing the values of the initializers. If you don't need to change the content, you can do this from StrataCode code:
file: doc/core/formatter.sc
formatter {
  firstName = "Conan";
  lastName = "OBrien";
}
You can imports types by placing this at the top of the file:
file: doc/core/formatter.schtml
<%@ import java.util.List; %>

Adding New File Formats

Adding a new file format that uses the template language requires only a few lines of code in a layer definition file. You add and configure a new TemplateProcesor instance and register it for a given syntax, and optional path prefix. For each extension, you specify whether the template is interpreted or compiled, what extension is produced, what directory it goes in, and what build-pass this all occurs in.

To interpret a template at runtime from your code, use the parser to parse the template. It returns a Template instance. You can evaluate that template, providing an optional this object or other context, then interpret the Java code inside to produce the string.

When you compile a template, the code is transformed into a .java file and compiled.

Examples

For more examples of the template language, look at "doc/core" layer where StrataCode's documentation is defined, or the editor/js/core schtml and scss files.

Also look at WEB-INF/web.scxml in the layers: servlet/webApp and wicket/lib. This shows how you can layer dynamic generation of XML files, driven from annotations in Java code.