A super-small Java library for templating, i.e. generating text files (HTML, XML, whatever) from a "template" text file and dynamic data.
It is based on the concept that the templates are bare Java™ classes.
Other templating engines like Freemarker or Java ServerPages (a.k.a. "JSP") use template files written their special markup language, optimized for the particular purpose.
This has the following problems:
- Each framework has its own syntax and semantics for implementing the dynamic part of a document (variables, control structures, in-source documentation, ...).
- Because the nesting of control structures and the indentation of the static content are generally not identical, it is very hard to write easily-readable code.
- Most templating frameworks have no static typing, i.e. you must always be careful when using (or worse: re-using) variables, because there is no "compile-time checking" of types.
- Powerful debuggers are not available in many cases, so you typcially revert to looking at log files.
- Because there are so many different templating engines, you favorite IDE possibly offers no advanced editor with code completion, refactoring, and the like.
- If the templating engine involves some kind of an "translation" step, then that must be executed on each and every code change - at worst manually.
The approach of no-template is as simple as it could be:
// By convention, you place all your template classes in packages named "*.templates[.*]":
package com.acme.myproject.templates;
import de.unkrig.notemplate.*;
// Your template class extends the "NoTemplate" class, either directly, as here, or indirectly to get
// extra convenience functionality useful for a particular purpose, e.g. by extending "HtmlTemplate".
//
// The name of the template class is typically chosen to reflect the "name" of the generated document
// (whatever "name" means in the specific context). For example, the name of this template class
// indicates that the name of its product is "index.html".
public
class IndexHtml extends NoTemplate {
// Your template class has only a zero-arg constructor (or no constructor at all).
// It (typically) declares no fields.
// You declare a method (typically, but not necessarily) named "render" with all the parameters
// necessary for the dynamic data that it needs.
public void
render(String firstName, String lastName, int age) {
// The method "NoTemplate.l(String...)" writes the given strings to the output, each followed
// by a line break.
//
// By putting each argument on a separate line, and aligning these lines in column one, the
// indentation in the generated document is exactly reflected here in the source code (apart
// from the enclosing quotes).
l(
"<!DOCTYPE html>",
"<html>",
" <head>",
" <title>My first template</title>",
" </head>",
" <body>",
" This is my very first template.",
" <br />",
" Hello " + firstName + " " + lastName + ",", // Notice how the method parameters are used.
" you must be " + age + " years old!",
" </body>",
"</html>"
);
}
// Examples of how to use the template class declared above:
// Render to STDOUT:
NoTemplate.newTemplate(IndexHtml.class, System.out).render("John", "Doe", 99);
// Render file "index.html":
NoTemplate.render(IndexHtml.class, new File("index.html"), (IndexHtml t) -> {
t.render("John", "Doe", 99);
});
No-Template addresses most of the above-mentioned problems:
- You don't need to learn yet another syntax - it's all Java! Honestly, HTML, JavaScript, CSS, ... is already enough to confuse me.
- The "content" lines are quite easy to spot in the source code, since they all appear in column one of the source code.
- You benefit from the static type model of Java, and get compiler warnings and errors, whereas other frameworks produce only runtime errors.
- Debugging of No-Templates integrates seamlessly with the "normal" Java classes that make up your program.
- All the nifty Java IDE features like syntax highlighting, automatic indentation, autocompletion, code checking as-you-type, code refactoring and so forth work just the same for No-Template classes.
- Last but not least: There is no "translation" step whatsoever that you need to integrate with your build process and continuous integration - it's all plain Java.
However, where is light, there must also be shadow:
-
Literal text (which maps to Java string literals) must be enclosed in quotes, and some characters must
(as you all know) be escaped with backslashes. This is particularly annoying for double quotes
```java
l(
" Look <a href=\"" + href + "\">here</a>".
);
```
and for embedded JavaScript code.
One possible workaround is to use single quotes instead of double quotes, which both HTML and JavaScript permit. (And single quotes need not be backslash-escaped in Java string literals.)
Another is to use theMakeClass
command line utility, which converts an "example file" into a template class with one big "l()
" call. -
Embedding variables is not as compact as in other templating languages, where you can typically write
"
${myvar}
". - My favorite IDE (of course) supports syntax highlighting, code checking and autocompletion in the Java code, but not (yet) in the HTML code in the string literals.
- The number of string literals per Java class is limited to 64k, and consequently the size of a template class. As a last resort, one can split the code generation into multiple templates which include each other.
-
Code checkers may complain about the "wrong" indentation of the arguments to the "
l()
" method. You need to suppress these message, or, even better, verify that the arguments of "l()
" indeed start in column one.
Other concepts that are not demostrated in the above example are:
-
Within the renderer, you can use local variables, control structures, constants, etc., as in any Java™
method.
- You can declare methods ("subrenderers") that are invoked from the renderer, which also render text. By convention, the name of a subrenderer method starts with a small "r", followed by an upper-case letter. By moving code from the renderer into subrenderers, you can implement a logical structure for your template, to any level you want.
-
To encapsulate code that is re-used by multiple templates, you'd move it into "subtemplates" and include it
from the top-level template with
```java
this.include(MySubtemplateHtml.class).render( /* ... */ );
```
Again, the subtemplate declares a method named "render" with all the parameters that it requires to do the job.
To keep the (top-level) template classes and the subtemplate classes separate, you'd put them in a package "*.templates.include" (or a subpackage thereof, if you feel the urge to add even more structure).
The "JAVADOC doclet" (an open-source reimplementation of the standard JDK JAVADOC doclet) makes heavy use of No-Template and is a good source for inspiration.
No-Template is available for download here.
-
Recent, yet unreleased changes:
-
-
Version 1.0.3, 2016-11-25:
-
The printing of the "Generating..." message to System.out is now conditional (by a "boolean quiet" parameter).
-
The "old" (pre-MAVEN) source directory tree was still there.
-
Modified the text of the copyright notice slightly: Replaced "author" with "copyright holders and contributors".
-
Version 1.0.2, 2016-11-07:
-
Switched from de-unkrig-commons 1.1.12 to 1.2.2.
-
Version 1.0.0, 2016-10-14:
-
First official release.
-
Changed build process from ANT to MAVEN.
The
MakeClass
command line utility
No-Template is distributed under the New BSD License.